--- /dev/null
+#!/usr/bin/perl
+# Name : euse
+# Purpose : Command line USE flag editor
+# Author : Arun Bhanu
+# Version : 0.1.0
+# Copyright : GNU GPL v2
+
+use warnings;
+use strict;
+
+use Getopt::Long;
+use File::Copy;
+use Env qw(USE);
+
+# Define constants
+use constant FLAG_TYPE=>['G', 'L'];
+use constant FLAG_MASK=>['+', '-', 'M'];
+use constant FLAG_SITE=>['E', 'C', 'A', 'D', ' '];
+use constant SITE_DESC=>['environment', 'make.conf', 'auto', 'make.defaults'];
+
+# parse_wo_args & parse_with_args populate command line arguments into
+# these. The argument and variable names has to match because we make use
+# of symbolic references. Yeah, I know these are not good names.
+our $h = 0; # -h|help, boolean
+our $v = 0; # -v|version, boolean
+our $c = 0; # -c|conf, boolean
+our $d = 0; # -d|defaults, boolean
+our $e = 0; # -e|env, boolean
+our @i = (); # -i|info, info accepts many optional values.
+our @p = (); # -p|package argument, package accepts many values.
+our @E = (); # -E|enable, enable use flags in make.conf
+our @D = (); # -D|disable, disable use flags in make.conf. Not a physical
+ # delete, but just minus sign in front of the given use flag.
+
+# arguments that does not take values.
+our $Args_Wo_Param = "hvcde";
+our $Non_Arg = " ";
+our $Curr_Arg; # Argument being parsed.
+our %Unknown_Args; # All unknown arguments are populate here.
+
+our %Uf_warnings;
+
+our $Version = "0.1.0";
+
+# Paths to all the reqd files
+our $FMake_conf = "/etc/make.conf";
+our $FMake_defaults = "/etc/make.profile/make.defaults";
+our $FUse_desc = "/etc/make.profile/../use.desc";
+our $FUse_local = "/etc/make.profile/../use.local.desc";
+our $FUse_mask = "/etc/make.profile/use.mask";
+
+# For my local testing
+#our $FMake_conf = "make.conf"; #/etc/make.conf
+#our $FMake_defaults = "make.defaults"; #/etc/make.profile/make.defaults
+#our $FUse_desc = "use.desc"; #$PORTDIR/profiles/use.desc
+#our $FUse_local = "use.local.desc";#$PORTDIR/profiles/use.local.desc
+#our $FUse_mask = "use.mask";#/etc/make.profile/use.mask
+
+#BEGIN Main
+MAIN: {
+ Getopt::Long::Configure("no_ignore_case");
+ # help, version does not accept arguments
+ # info accepts optional agruments.
+ # package requires arguments.
+ GetOptions(
+ "h|help" => \&parse_wo_args, # Show usage info.
+
+ "v|version" => \&parse_wo_args, # Show version info.
+
+ "i|info:s" => \&parse_with_args, # Show use flags info.
+ # Show all use flags if none
+ # specified. (Optional values)
+
+ #"p|package:s" => \&parse_with_args, # Show use flag info for all
+ # given packages (Mandatory values)
+
+ "E|enable:s" => \&parse_with_args, # enable use flag from make.conf
+ # e.g. mozilla
+
+ "D|disable:s" => \&parse_with_args, # disable use flag from make.conf
+ # e.g. -mozilla.
+
+ "c|conf" => \&parse_wo_args, # print the contents of make.conf
+ # use flag setting
+
+ "d|defaults" => \&parse_wo_args, # print the contents of
+ # make.defaults use flag setting
+
+ "e|env" => \&parse_wo_args, # print the contents of
+ # environment use flag setting
+
+ "<>" => \&handle_unknown # all unknown arguments.
+ ) or usage();
+
+
+ # If no options are entered show usage.
+ if ((my $opts = $h +
+ $v +
+ ($c or $d or $e) +
+ defined(@i) +
+ defined(@p) +
+ (defined(@E) or defined(@D))) < 1) {
+ usage();
+ }
+
+ # If any arguments are provided for options which are boolean show usage.
+ if (%Unknown_Args) {
+ print ("Unknown arguments: \n");
+ foreach (sort keys %Unknown_Args) {
+ print "\t$_ => " . join (" ", @{$Unknown_Args{$_}}) . "\n";
+ }
+ usage();
+ }
+
+ # Only one of -h, -v, -i, -p, -c/-d/-e or -E/-D allowed
+ if ((my $opts = $h +
+ $v +
+ ($c or $d or $e) +
+ defined(@i) +
+ defined(@p) +
+ (defined(@E) or defined(@D))) > 1) {
+ usage("Only one of -h, -v, -i, -p or -E/-D allowed");
+ }
+
+ # Mandatory argument field check if arguments have been entered
+ my @reqd_args = ();
+ if (defined(@E)) {
+ if ($E[0] eq '') {
+ push @reqd_args, "-E";
+ }
+ }
+ if (defined(@D)) {
+ if ($D[0] eq '') {
+ push @reqd_args, "-D";
+ }
+ }
+ if ($#reqd_args >= 0) {
+ print join(", ", @reqd_args), " require argument(s). \n";
+ usage();
+ }
+
+ # If -h,--help is entered show usage.
+ if ($h) {
+ #check_permissions("rw"); #just for testing purposes
+ usage();
+ }
+
+ # If -v,--version is entered show version.
+ if ($v) {
+ version();
+ }
+
+ if ($c || $d || $e) {
+ #Check the permissions for all the reqd files and sets their path
+ #If not enough permission don't proceed.
+ check_permissions("r");
+ if ($d) {
+ my $defaults_uf_list_ref = parse_make($FMake_defaults);
+ print "USE setting in make.defaults: \n";
+ print (join (" ", @$defaults_uf_list_ref) . "\n\n");
+ }
+
+ if ($c) {
+ my $conf_uf_list_ref = parse_make($FMake_conf);
+ print "USE setting in make.conf: \n";
+ print (join (" ", @$conf_uf_list_ref) . "\n\n");
+ }
+
+ if ($e) {
+ my $env_uf_list_ref = parse_env($USE);
+ print "USE setting in environment: \n";
+ print (join (" ", @$env_uf_list_ref) . "\n\n");
+ }
+ exit(0);
+ }
+
+ check_permissions("r");
+
+ # Read /usr/portage/profiles/use.desc
+ my %use_flags;
+ parse_use($FUse_desc, \%use_flags);
+
+ # Read /usr/portage/profiles/use.local.desc
+ parse_use_local($FUse_local, \%use_flags);
+
+ # Read /etc/make.profile/use.mask, if exists.
+ parse_use_mask($FUse_mask, \%use_flags) if (-e $FUse_mask);
+
+ # If a -* is found in env, make.conf & make.defaults is skipped.
+ # If a -* is found in make.conf, make.defaults is skipped.
+ # If a -* is found in all the elements in front of -* is skipped.
+ # In each of the case all the elements in front of -* is also skipped.
+ # Read ENV{USE}
+ my (@env_uf_list, @conf_uf_list, @defaults_uf_list) = ();
+ my $env_uf_list_ref = \@env_uf_list;
+ my $conf_uf_list_ref = \@conf_uf_list;
+ my $defaults_uf_list_ref = \@defaults_uf_list;
+
+ $env_uf_list_ref = parse_env($USE) if (defined($USE));
+ unless (is_all_disabled($env_uf_list_ref)) {
+ # Read /etc/make.conf
+ $conf_uf_list_ref = parse_make($FMake_conf);
+
+ unless (is_all_disabled($conf_uf_list_ref)) {
+ # Read /etc/make.profile/make.defaults
+ $defaults_uf_list_ref = parse_make($FMake_defaults);
+ }
+ }
+
+ # is this the correct way of check the length of an array reference??
+ parse_use_flag($defaults_uf_list_ref, \%use_flags, 3)
+ unless ($#{@$defaults_uf_list_ref} < 0);
+ parse_use_flag($conf_uf_list_ref, \%use_flags, 1)
+ unless ($#{@$conf_uf_list_ref} < 0);
+ parse_use_flag($env_uf_list_ref, \%use_flags, 0)
+ unless ($#{@$env_uf_list_ref} < 0);
+
+
+ # If -i, --info is entered print all USE flags if no arguments given or
+ # print the description for the given USE flags.
+ if (defined(@i)) {
+
+ #if -i mozilla mozilla is entered the extra mozilla is removed.
+ remove_duplicates(\@i);
+
+ my @use_flag_list = ();
+ my $max_len = 0;
+ my $curr_key_len = 0;
+
+ # Retrieve the objects to be send to the formatting function as an
+ # array.
+ if ($i[0] eq '') {
+ #Display all USE flags
+ foreach (sort keys %use_flags) {
+ $curr_key_len = length($_);
+ $max_len = $curr_key_len if $curr_key_len > $max_len;
+ push @use_flag_list, $use_flags{$_};
+ }
+ }
+ else {
+ #Display USE flags for given arguments
+ foreach (sort @i) {
+ if (exists($use_flags{$_})) {
+ $curr_key_len = length($_);
+ $max_len = $curr_key_len if $curr_key_len > $max_len;
+ push @use_flag_list, $use_flags{$_} ;
+ }
+ else {
+ # Invalid use flag
+ add_warning($_, "Querying an invalid use flag.");
+ }
+ }
+ }
+
+ format_output(\@use_flag_list, \$max_len);
+ format_warning();
+ exit(0);
+ }
+
+ # Enable USE flags and disable USE flags
+ if (defined(@E) || defined(@D)) {
+
+ check_permissions("rw");
+
+ # Enable the last occurence of the USE flag. Check if already exists
+ # in make.conf. If exist and enabled leave it alone. If exists and
+ # disabled enable it. If not found, put an entry at the end. Why end?
+ # -* may be in effect somewhere in the USE flag. So adding in at the
+ # end will make sure it gets enabled. euse -E "*" or euse -E all will
+ # look for a -* in make.conf. If found removes it.
+ if (defined(@E)) {
+ remove_duplicates(\@E);
+ rearrange_args(\@E);
+ enable_use_flag($conf_uf_list_ref, \%use_flags);
+ }
+
+ # Disable the last occurence of the USE flag.
+ # euse -D "*" will put a -* at the end of USE flag setting in make.conf.
+ if (defined(@D)) {
+ remove_duplicates(\@D);
+ rearrange_args(\@D);
+ disable_use_flag($conf_uf_list_ref, \%use_flags);
+ }
+ format_warning();
+ exit(0);
+ }
+ exit(0);
+}
+#END Main
+
+# Show usage. Accepts optional argument. If given prints them as warning msg.
+sub usage {
+ warn "@_\n" if @_;
+ die <<EOF;
+'*' leading an option indicates not-yet-implemented
+Usage:
+ -h, --help - print this message
+ -v, --version - print version information
+ -i, --info - print USE flag information for the the given USE flags.
+ Optional argument.
+ -c, --conf - print USE flag setting in make.conf
+ -d, --defaults - print USE flag setting in make.defaults
+ -e, --env - print USE flag setting in environment variable USE
+ *-p, --package - requires a package name to be given for which you
+ want to see USE flags.
+ -E, --enable - enable use flag to make.conf e.g. -E mozilla puts mozilla
+ in make.conf. Mandatory argument.
+ -D, --disable - disable use flag from make.conf e.g. -D mozilla
+ puts -mozilla in make.conf. Mandatory argument.
+
+Please see the README file for more details.
+
+EOF
+
+}
+
+# Show version
+sub version
+{
+ die <<EOF;
+EUSE version $Version
+Copyright (C) 2003 - Arun Bhanu
+This program may be freely distributed under the terms of the GNU GPL v2.
+
+EOF
+
+}
+
+# check permissions for all reqd files
+sub check_permissions
+{
+ my ($r, $w) = split(//, shift());
+
+ #make.conf requires rw permissions. all the other files require read
+ #permission
+ my $errors = "";
+
+ # for make.conf
+ if ((defined($r) && $r eq "r") && (defined($w) && $w eq "w")) {
+ #print "rw defined \n";
+ unless (-e $FMake_conf && -r _ && -w _) {
+ $errors .= "requires read,write permissions for $FMake_conf. \n";
+ }
+ }
+ elsif (defined($r) && $r eq "r") {
+ #print "r defined \n";
+ unless (-e $FMake_conf && -r _) {
+ $errors .= "requires read,write permissions for $FMake_conf. \n";
+ }
+ }
+
+ # for all the other files
+ if (defined($r) && $r eq "r") {
+ #print "r defined \n";
+ unless (-e $FMake_defaults && -r _) {
+ $errors .= "requires read permissions for $FMake_defaults. \n";
+ }
+ unless (-e $FUse_desc && -r _) {
+ $errors .= "requires read permissions for $FUse_desc. \n";
+ }
+
+ unless (-e $FUse_local && -r _) {
+ $errors .= "requires read permissions for $FUse_local. \n";
+ }
+
+ if (-e $FUse_mask) {
+ unless (-r $FUse_mask) {
+ $errors .= "requires read permissions for $FUse_mask. \n";
+ }
+ }
+ }
+
+ die "EUSE exiting with following errors: \n$errors" unless ($errors eq "");
+}
+
+# clean up reqd
+sub handle_unknown
+{
+ no strict 'refs';
+ if (defined $Curr_Arg) {
+ my $option = $Curr_Arg;
+ if ($option =~ /[$Args_Wo_Param]/) {
+ #print "param not required \n";
+ #print "Option: $option " . join (",", @_) . "\n";
+ push @{$Unknown_Args{$option}}, @_;
+ }
+ else {
+ #print "param is okay \n";
+ push @$option, @_;
+ }
+ }
+ else {
+ #print "curr flag not defined \n";
+ push @{$Unknown_Args{$Non_Arg}}, @_;
+ }
+}
+
+sub parse_with_args
+{
+ no strict 'refs';
+ my ($option, $value) = @_;
+ #print "parse with handle: " . $option . " " . $value . "\n";
+
+ push @$option, $value; #do not allow comma separated.
+ $Curr_Arg = $option;
+
+}
+
+sub parse_wo_args
+{
+ no strict 'refs';
+ my ($option, $value) = @_;
+ #print "parse wo handle: " . $option . " " . $value . "\n";
+ $$option = $value;
+ $Curr_Arg = $option;
+}
+
+# Generic subroutine to read a file.
+# Returns an array of lines in array context or returns the whole file in a
+# single scalar in scalar context.
+sub read_file {
+ my $file_name = shift();
+ if (open FILE, $file_name) {
+ local $/ = undef unless wantarray;
+ return <FILE>;
+ }
+ else {
+ die "Couldn't read file $file_name. $! \n";
+ }
+ #return; # returns undef in scalar context, () in list context
+}
+
+sub parse_use
+{
+ my $file_name = shift();
+ my $use_flags_ref = shift();
+ my @lines = read_file($file_name);
+ chomp @lines;
+ foreach (@lines) {
+ if (/^\s*([-a-z0-9_+]+)\s+-\s*(.*)$/i) {
+ $use_flags_ref->{$1} = create_use_flag($1, $2, FLAG_TYPE->[0]);
+ }
+ }
+}
+
+sub parse_use_local
+{
+ my $file_name = shift();
+ my $use_flags_ref = shift();
+ my @lines = read_file($file_name);
+ chomp @lines;
+ foreach (@lines) {
+ if (m{^\s*([-a-z0-9_/+]*):([-a-z0-9_/+]*)\s*-\s*(.*)$}i)
+ {
+ my $pkg_name = $1;
+ my $use_flag = $2;
+ my $use_desc = $3;
+ $use_flags_ref->{$use_flag} =
+ create_use_flag($use_flag, $use_desc, FLAG_TYPE->[1],
+ FLAG_MASK->[1], $pkg_name);
+ }
+ }
+}
+
+sub parse_use_mask
+{
+
+ my $file_name = shift();
+ my $use_flags_ref = shift();
+ my @lines = read_file($file_name);
+ chomp @lines;
+ my @use_mask;
+ foreach (@lines) {
+ if (m/^\s*([-a-z0-9_+]+)\s*$/i)
+ {
+ my $use_flag = $1;
+ if (exists $use_flags_ref->{$use_flag}) {
+ $use_flags_ref->{$use_flag}->[3] = FLAG_MASK->[2];
+ }
+ else {
+ add_warning($use_flag, "Found in use.mask, but is not a valid use flag.");
+ }
+ }
+ }
+
+
+ #return \@use_mask;
+}
+
+sub add_warning
+{
+ my $use_flag = shift();
+ my $msg = shift();
+ if (exists $Uf_warnings{$use_flag}) {
+ push @{$Uf_warnings{$use_flag}}, $msg;
+
+ }
+ else {
+ my @warn_list = ($msg);
+ $Uf_warnings{$use_flag} = \@warn_list;
+ }
+}
+
+sub parse_make
+{
+ my $file_name = shift();
+ my $lines = read_file($file_name);
+ my @site_uf_list;
+ if ($lines =~ m/^\s*USE[\s\n]*=[\s\n]*"([^"]*)"/ms)
+ {
+ my $use_var = $1;
+ $use_var =~ s/[\r\n]/ /g;
+ @site_uf_list = split(' ', $use_var);
+ }
+ #print ("Make: \n" . join (" ", @site_uf_list) . "\n");
+ return \@site_uf_list;
+}
+
+sub parse_env
+{
+ my $env = shift();
+ $env = "" unless (defined($env));
+ #print "USE: $env \n";
+ my @site_uf_list = split(' ', $env);
+ #print ("Env: " . join (" ", @site_uf_list) . "\n");
+ return \@site_uf_list;
+}
+
+sub parse_use_flag
+{
+ my $site_uf_list_ref = shift();
+ my $use_flags_ref = shift();
+ my $site_index = shift();
+
+ #print "Flag site: $site_index -> " . FLAG_SITE->[$site_index] . "\n";
+ #print "Flag desc: $site_index -> " . SITE_DESC->[$site_index] . "\n";
+ my ($isenabled, $use_wo_sign, $use_with_sign);
+ # recurse from back to front. if -* is found the rest of the elements in
+ # front is ignored.
+ for (my $i = $#{@$site_uf_list_ref}; $i >= 0; $i--) {
+ my $curr_flag = $site_uf_list_ref->[$i];
+ #print "curr flag: $curr_flag \n";
+ last if ($curr_flag =~ m/^-\*$/);
+ $isenabled = 1; # sign = 1 is enabled, sign = 0 is disabled.
+ $use_wo_sign = $use_with_sign = $curr_flag;
+ if ($use_with_sign =~ /^-(.*)/) {
+ $use_wo_sign = $1;
+ $isenabled = 0;
+ }
+
+ if (exists $use_flags_ref->{$use_wo_sign}) {
+ $use_flags_ref->{$use_wo_sign}->[5] = FLAG_SITE->[$site_index];
+ if ($use_flags_ref->{$use_wo_sign}->[3] eq FLAG_MASK->[2]) {
+ add_warning($use_wo_sign, "$use_with_sign found in " .
+ SITE_DESC->[$site_index] . " is masked. You shouldn't be " .
+ "setting this use flag.");
+ }
+ #if ($use_flags_ref->{$use_wo_sign}->[3] eq FLAG_MASK->[3] ) {
+ # add_warning($use_wo_sign, "$use_with_sign found in " .
+ # SITE_DESC->[$site_index] . " is an internal flag. You " .
+ # "shouldn't be setting this use flag.");
+ #}
+ if ($isenabled) {
+ $use_flags_ref->{$use_wo_sign}->[3] = FLAG_MASK->[0];
+ }
+ else {
+ $use_flags_ref->{$use_wo_sign}->[3] = FLAG_MASK->[1];
+ }
+
+ }
+ else {
+ add_warning($use_wo_sign, "$use_with_sign found in " .
+ SITE_DESC->[$site_index] . " not a valid use flag.");
+ }
+ }
+}
+
+# checks if -* is found in any given site - environment, make.conf,
+# make.defaults
+# Return 1 if -* is found, return 0 otherwise.
+sub is_all_disabled
+{
+ my $site_uf_list_ref = shift();
+ #print (join (" ", @$site_uf_list_ref) . "\n");
+ foreach (@$site_uf_list_ref) {
+ return 1 if (m/^-\*$/);
+ }
+ return 0;
+}
+
+sub enable_use_flag
+{
+ my $conf_uf_list_ref = shift();
+ my $use_flags_ref = shift();
+ # if -* is found in env, should we enable the flag in environment instead
+ # of make.conf
+ #print "Enabling in make.conf... \n";
+ my @add_flags;
+ my @del_flags;
+ my $enable_all = 0;
+ #my $disable_start = 0;
+ my $disable_found = 0;
+ EFLAG: foreach my $eflag (@E) {
+ if ($eflag eq "*" or $eflag eq "all") {
+ #print "$eflag need to del -* use flag.. \n";
+ $enable_all = 1;
+ $eflag = "*";
+ push @del_flags, $eflag;
+ foreach my $cflag (@$conf_uf_list_ref) {
+ if ($cflag eq "-*") {
+ $disable_found = 1;
+ last;
+ }
+ }
+ next EFLAG;
+ }
+ if (exists $use_flags_ref->{$eflag}) {
+ if ($use_flags_ref->{$eflag}->[3] eq FLAG_MASK->[2]) {
+ add_warning($eflag, "$eflag is masked. Enabling a masked " .
+ "flag.");
+ next EFLAG;
+ }
+ my $count = 0;
+ for (my $i = $#{@$conf_uf_list_ref}; $i >= 0; $i--) {
+ my $curr_flag = $conf_uf_list_ref->[$i];
+ #print "curr flag: $curr_flag eflag: $eflag \n";
+
+ if ($curr_flag eq $eflag) {
+ #print "flag exists.. \n";
+ next EFLAG;
+ }
+
+ my $eflag_sign = "-".$eflag;
+ if ($curr_flag eq $eflag_sign) {
+ #print "need to del use flag.. \n";
+ push @del_flags, $eflag;
+ next EFLAG;
+ }
+
+ if ($curr_flag eq "-*") {
+ $count++;
+ if ($enable_all) {
+ if ($disable_found && ($count == 1)) {
+ next;
+ }
+ else {
+ push @add_flags, $eflag;
+ next EFLAG;
+ }
+ }
+ else {
+ push @add_flags, $eflag;
+ next EFLAG;
+ }
+
+ }
+ }
+ #print "$eflag - need to enable this flag...\n";
+ push @add_flags, $eflag;
+ }
+ else {
+ add_warning($eflag, "Enabling invalid use flag.");
+ }
+ }
+
+ # should I use timestamp instead of .old suffix?
+ if (@add_flags || @del_flags) {
+
+ my $make_conf = read_file($FMake_conf);
+ #my $timestamp = get_timestamp();
+ #copy ($FMake_conf, "$FMake_conf.$timestamp");
+ copy ($FMake_conf, "$FMake_conf.bkeuse");
+ if (@add_flags) {
+ #print "these are flags to add...\n";
+ my $eflags = join (" ", @add_flags);
+ #print ($eflags . "\n");
+ $make_conf =~
+ s/(^\s*USE[\s\n]*=[\s\n]*")([^"]*)(")/$1$2 $eflags$3/ms;
+
+ # If there is no USE flag setting in make.conf include it and
+ # add the flags.
+ unless (defined($1)) {
+ #print "There are flags to add but no USE setting \n";
+ $make_conf =~ s/(^\s*#\s*Host)/USE="$eflags"\n$1/ms;
+ }
+ }
+
+ my $use_setting = "";
+ if (@del_flags) {
+ #print "these are flags to del...\n";
+ my $dflags = join (" ", @del_flags);
+ #print ($dflags . "\n");
+
+ if ($make_conf =~ /^\s*USE[\s\n]*=[\s\n]*"([^"]*)"/ms) {
+ $use_setting = $1;
+ }
+ foreach my $dflag (@del_flags) {
+ if ($dflag eq "*") {
+ $use_setting =~ s/^(.*)-\*(.*)$/$1$2/ms;
+ }
+ else {
+ # \Q, \E prevents flags like libg++ to bonk
+ $use_setting =~ s/^(.*)(-\Q$dflag\E)(.*)$/$1$dflag$3/ms;
+ }
+ }
+ $make_conf =~
+ s/(^\s*USE[\s\n]*=[\s\n]*")([^"]*)(")/$1$use_setting$3/ms;
+ }
+ open(OUTFILE, ">$FMake_conf") or
+ die("couldn't open make.conf");
+ print OUTFILE $make_conf;
+ close OUTFILE;
+ }
+ print "USE setting in make.conf after enabling: \n";
+ my $make_conf = read_file($FMake_conf);
+ my $use_setting = "";
+ $make_conf =~ /^\s*USE[\s\n]*=[\s\n]*"([^"]*)"/ms;
+ if (defined($1)) {
+ $use_setting = $1;
+ }
+ #print "$use_setting \n";
+ print (join(" ", split(' ', $use_setting)));
+ print "\n";
+}
+
+sub disable_use_flag
+{
+ my $conf_uf_list_ref = shift();
+ my $use_flags_ref = shift();
+ my @add_flags;
+ my @del_flags;
+ my $disable_all = 0;
+ #my $disable_start = 0;
+ my $disable_found = 0;
+
+ foreach my $cflag (@$conf_uf_list_ref) {
+ if ($cflag eq "-*") {
+ $disable_found = 1;
+ last;
+ }
+ }
+
+ DFLAG: foreach my $dflag (@D) {
+
+ if ($dflag eq "*" or $dflag eq "all") {
+ #print "$dflag need $disable_found to add -* use flag.. \n";
+ $disable_all = 1;
+ $dflag = "-*";
+ push @del_flags, $dflag if ($disable_found);
+ next DFLAG;
+ }
+
+ if (exists $use_flags_ref->{$dflag}) {
+ if ($use_flags_ref->{$dflag}->[3] eq FLAG_MASK->[2]) {
+ add_warning($dflag, "$dflag is masked. Disabling a masked " .
+ "flag.");
+ next DFLAG;
+ }
+
+ for (my $i = $#{@$conf_uf_list_ref}; $i >= 0; $i--) {
+ my $curr_flag = $conf_uf_list_ref->[$i];
+ #print "curr flag: $curr_flag dflag: $dflag \n";
+
+ if ($disable_all) {
+ if ($disable_found) {
+ push @del_flags, "-".$dflag;
+ next DFLAG;
+ }
+ else {
+ my $dflag_sign = "-".$dflag;
+ if ($curr_flag eq $dflag_sign) {
+ #print "flag exists.. \n";
+ next DFLAG;
+ }
+ if ($curr_flag eq $dflag) {
+ push @add_flags, $dflag;
+ next DFLAG;
+ }
+ }
+ }
+ else {
+ my $dflag_sign = "-".$dflag;
+ if ($curr_flag eq $dflag_sign) {
+ #print "flag exists.. \n";
+ next DFLAG;
+ }
+ if ($curr_flag eq $dflag) {
+ push @add_flags, $dflag;
+ next DFLAG;
+ }
+
+ if ($disable_found) {
+ if ($curr_flag eq "-*") {
+ push @del_flags, "-".$dflag;
+ next DFLAG;
+ }
+ }
+ }
+ }
+ unless ($disable_found) {
+ #print "$dflag - need to disable this flag...\n";
+ push @del_flags, "-".$dflag;
+ }
+ }
+ else {
+ add_warning($dflag, "Disabling invalid use flag.");
+ }
+ }
+
+ my $make_conf = read_file($FMake_conf);
+ copy ($FMake_conf, "$FMake_conf.bkeuse");
+ if ($disable_all && !$disable_found) {
+ $make_conf =~
+ s/(^\s*USE[\s\n]*=[\s\n]*")([^"]*)(")/$1-* $2$3/ms;
+ }
+
+ if (@add_flags || @del_flags) {
+ #my $timestamp = get_timestamp();
+ #copy ($FMake_conf, "$FMake_conf.$timestamp");
+
+ if (@del_flags) {
+ print "these are flags to disable...\n";
+ my $dflags = join (" ", @del_flags);
+ #print ($dflags . "\n");
+ $make_conf =~
+ s/(^\s*USE[\s\n]*=[\s\n]*")([^"]*)(")/$1$2 $dflags$3/ms;
+
+ # If there is no USE flag setting in make.conf include it and
+ # disable the flags.
+ unless (defined($1)) {
+ print "There are flags to del but no USE setting \n";
+ $make_conf =~ s/(^\s*#\s*Host)/USE="$dflags"\n$1/ms;
+ }
+ }
+
+ my $use_setting = "";
+ if (@add_flags) {
+ #print "these are flags to enable...\n";
+ my $eflags = join (" ", @add_flags);
+ #print ($eflags . "\n");
+ if ($make_conf =~ /^\s*USE[\s\n]*=[\s\n]*"([^"]*)"/ms) {
+ $use_setting = $1;
+ }
+ foreach my $eflag (@add_flags) {
+ # should only enable the last one occurence
+ # Is there are better regex for this to avoid backtracking?
+ # \Q, \E prevents flags like libg++ to bonk
+ $use_setting =~ s/(.*)\Q$eflag\E(.*)/$1-$eflag$2/s;
+ }
+ $make_conf =~
+ s/(^\s*USE[\s\n]*=[\s\n]*")([^"]*)(")/$1$use_setting$3/ms;
+
+ }
+
+ }
+
+ open(OUTFILE, ">$FMake_conf") or
+ die("couldn't open make.conf");
+ print OUTFILE $make_conf;
+ close OUTFILE;
+
+ print "USE setting in make.conf after disabling: \n";
+ $make_conf = read_file($FMake_conf);
+ my $use_setting = "";
+ $make_conf =~ /^\s*USE[\s\n]*=[\s\n]*"([^"]*)"/ms;
+ if (defined($1)) {
+ $use_setting = $1;
+ }
+ #print "$use_setting \n";
+ print (join(" ", split(' ', $use_setting)));
+ print "\n";
+}
+
+# is there a better way of rearranging? This works, but any better solutions?
+sub rearrange_args
+{
+ my $arg_array_ref = shift();
+ my @new_arg_array = ();
+ foreach my $arg (@$arg_array_ref) {
+ if ($arg eq "*" or $arg eq "all") {
+ unshift @new_arg_array, $arg;
+ }
+ else {
+ push @new_arg_array, $arg;
+ }
+ }
+ @$arg_array_ref = @new_arg_array;
+}
+
+# removes duplicate elements of an array
+sub remove_duplicates
+{
+ my $arg_array_ref = shift();
+ my %arg_hash;
+ my @unique_args = grep { !$arg_hash{$_} ++ } @$arg_array_ref;
+ @$arg_array_ref = @unique_args;
+}
+
+sub get_timestamp
+{
+ my ($sec, $min, $hour, $day, $mon, $year) = (localtime)[0..5];
+ $mon += 1;
+ $year += 1900;
+ # need to format hour, min, second, day, mon to 2 digits.
+ my $timestamp = sprintf ('%4u%02u%02u%02u%02u%02u',
+ $year, $mon, $day, $hour, $min, $sec);
+ print "$timestamp\n";
+ return $timestamp;
+}
+
+
+
+
+#array[name, desc, type, mask, pkg, site]
+# name - name of the use flag (Required)
+# desc - description for the use flag (Required)
+# type - where the use flag is found. Acceptable values (global,local). Global
+# means use.desc and local means use.local.desc (Required)
+# mask - whether the use flag is enabled, disabled or masked. Acceptable values
+# (enabled,disabled,masked,internal) (Optional)
+# pkg - for local use flags, package which it belongs to. For global use flags
+# - it is empty.
+# site - if the use flag is enabled where is it enabled. Acceptable values
+# (E,C,A,D,-) (Optional)
+# E - environment
+# C - make.conf
+# A - auto
+# D - make.defaults
+# - - not enabled
+sub create_use_flag(\$\$\$;\$\$\$)
+{
+ my ($name, $desc, $type) = ($_[0], $_[1], $_[2]);
+ my $mask = (defined($_[3]) ? $_[3] : FLAG_MASK->[1]);
+ my $pkg = (defined($_[4]) ? $_[4] : "");
+ my $site = (defined($_[5]) ? $_[5] : FLAG_SITE->[4]);
+ my @use_flag = ($name, $desc, $type, $mask, $pkg, $site);
+ #print (join (" ", @use_flag) . "\n");
+ return \@use_flag;
+}
+
+# to format all the warning messages
+sub format_warning
+{
+ if (%Uf_warnings) {
+ print "EUSE WARNINGS!! \n";
+ my $max_len = 0;
+ my $curr_key_len = 0;
+ foreach (sort keys %Uf_warnings) {
+ $curr_key_len = length($_);
+ $max_len = $curr_key_len if $curr_key_len > $max_len;
+ }
+
+ my ($use_flag_name, $warning_msg);
+
+ my $warn_format = "format WARNINGS_FORMAT= \n" .
+'@' . ('<'x($max_len-1)) . ' => ' . '^' . ('<'x(80-$max_len-4-1)) . "\n" .
+'$use_flag_name, $warning_msg' ."\n" .
+(' 'x($max_len+4)) . '^' . ('<'x(80-$max_len-4-1-2)) . '~~' . "\n" .
+'$warning_msg' . "\n" .
+".\n";
+ eval $warn_format;
+ #print $warn_format;
+
+ my $warn_bot_format = "format WARNINGS_BOT_FORMAT= \n" .
+(' 'x($max_len)) . ' => ' . '^' . ('<'x(80-$max_len-4-1)) . "\n" .
+'$warning_msg' . "\n" .
+(' 'x($max_len+4)) . '^' . ('<'x(80-$max_len-4-1-2)) . '~~' . "\n" .
+'$warning_msg' . "\n" .
+".\n";
+
+ eval $warn_bot_format;
+ #print $warn_bot_format;
+
+ foreach (sort keys %Uf_warnings) {
+ $use_flag_name = $_;
+ my $count = 0;
+ foreach (@{$Uf_warnings{$_}}) {
+ $~ = ($count++ == 0) ?
+ 'WARNINGS_FORMAT' : 'WARNINGS_BOT_FORMAT';
+ $warning_msg = $_;
+ write;
+ }
+ }
+ }
+}
+
+# formatted output for euse -i
+sub format_output
+{
+ my @use_flag_list = @{shift()};
+ my $max_len = ${shift()};
+ #print "Use flag list: $#use_flag_list \n";
+ #print "Key Length: $max_len \n";
+ if ($#use_flag_list < 0) {
+ print ("No matching use flags to display!! \n");
+ return;
+ }
+ my @use_flag;
+ my ($use_flag_name, $use_flag_desc, $use_flag_type, $use_flag_mask,
+ $use_flag_site);
+
+ # No of columns = 80
+ # -1 to compensate for < or @ char in format
+ # -2 to compensate for ~~ in format
+ # No of other chars excluding use flag name = 14
+ my $use_flag_format = "format USE_FLAG_FORMAT= \n" .
+'@' . ('<'x($max_len-1)) . ' [@]' . ' [@] [@] ' . '^' . ('<'x(80-$max_len-13-1)) . "\n" .
+'$use_flag_name, $use_flag_mask, $use_flag_site, $use_flag_type, $use_flag_desc' ."\n" .
+(' 'x($max_len+13)) . '^' . ('<'x(80-$max_len-13-1-2)) . '~~' . "\n" .
+'$use_flag_desc' . "\n" .
+".\n";
+
+ eval $use_flag_format;
+ #print $use_flag_format;
+
+
+ $~ = 'USE_FLAG_FORMAT';
+ foreach (@use_flag_list) {
+ @use_flag = @{$_};
+ $use_flag_name = $use_flag[0];
+ $use_flag_desc = ($use_flag[4] eq "" ?
+ $use_flag[1] : "[" . $use_flag[4] . "] : " . $use_flag[1]);
+ $use_flag_type = $use_flag[2];
+ $use_flag_mask = $use_flag[3];
+ $use_flag_site = $use_flag[5];
+ write;
+ }
+
+}
+
+# TODO:
+# 1. -p feature
+# 2. pipe the output to less to implement paging.
+# 3. flag to disable all warnings.
+# 4. filters - all global, local, enabled through make.conf,
+# make.defaults, env. All enabled flags, all disabled flags.
+# 5. combine -d, -c, -e with -i to give detailed output.
+#