-#!/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
-
-if (-f "/etc/make.profile/parent") {
- print "euse doesn't support cascading profiles yet\n";
- exit(0);
-}
-
-#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 "");
+#!/bin/bash
+
+# $Header$
+
+# bash replacement for the original euse by Arun Bhanu
+# Author: Marius Mauch <genone@gentoo.org>
+# Version: 0.2
+# Licensed under the GPL v2
+
+PROGRAM_NAME=euse
+PROGRAM_VERSION=0.1
+
+MAKE_CONF_PATH=/etc/make.conf
+MAKE_GLOBALS_PATH=/etc/make.globals
+MAKE_PROFILE_PATH=/etc/make.profile
+MAKE_CONF_BACKUP_PATH=/etc/make.conf.euse_backup
+
+MODE="showhelp" # available operation modes: showhelp, showversion, showdesc, showflags, modify
+
+parse_arguments() {
+ if [ -z "${1}" ]; then
+ return
+ fi
+ while [ -n "${1}" ]; do
+ case "${1}" in
+ -h | --help) MODE="showhelp";;
+ -v | --version) MODE="showversion";;
+ -i | --info) MODE="showdesc";;
+ -l | --local) SCOPE="local";;
+ -g | --global) SCOPE="global";;
+ -a | --active) MODE="showflags";;
+ -E | --enable) MODE="modify"; ACTION="add";;
+ -D | --disable) MODE="modify"; ACTION="remove";;
+ -P | --prune) MODE="modify"; ACTION="prune";;
+ -*)
+ echo "ERROR: unknown option ${1} specified."
+ echo
+ MODE="showhelp"
+ ;;
+ *)
+ ARGUMENTS="${ARGUMENTS} ${1}"
+ ;;
+ esac
+ shift
+ done
}
-# 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}}, @_;
- }
+error() {
+ echo "ERROR: ${1}"
+ exit 1
}
-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;
-
+check_sanity() {
+ # file permission tests
+ local descdir
+
+ descdir="$(get_portdir)/profiles"
+
+ [ ! -r "${MAKE_CONF_PATH}" ] && error "${MAKE_CONF_PATH} is not readable"
+ [ ! -r "${MAKE_GLOBALS_PATH}" ] && error "${MAKE_GLOBALS_PATH} is not readable"
+ [ ! -s "${MAKE_PROFILE_PATH}" ] && error "${MAKE_PROFILE_PATH} is not a symlink"
+ [ -z "$(get_portdir)" ] && error "\$PORTDIR couldn't be determined"
+ [ ! -d "${descdir}" ] && error "${descdir} does not exist or is not a directory"
+ [ ! -r "${descdir}/use.desc" ] && error "${descdir}/use.desc is not readable"
+ [ ! -r "${descdir}/use.local.desc" ] && error "${descdir}/use.local.desc is not readable"
+ [ ! -r "$(get_make_defaults)" ] && error "$(get_make_defaults) is not readable"
+ [ "${MODE}" == "modify" -a ! -w "${MAKE_CONF_PATH}" ] && error ""${MAKE_CONF_PATH}" is not writable"
}
-sub parse_wo_args
-{
- no strict 'refs';
- my ($option, $value) = @_;
- #print "parse wo handle: " . $option . " " . $value . "\n";
- $$option = $value;
- $Curr_Arg = $option;
+showhelp() {
+ echo "${PROGRAM_NAME} v${PROGRAM_VERSION}"
+ echo
+ echo "Syntax: ${PROGRAM_NAME} <option> [suboptions] [useflaglist]"
+ echo
+ echo "Options: -h, --help - show this message"
+ echo " -v, --version - show version information"
+ echo " -i, --info - show descriptions for the given useflags"
+ echo " -g, --global - show only global use flags (suboption)"
+ echo " -l, --local - show only local use flags (suboption)"
+ echo " -a, --active - show currently active useflags and their origin"
+ echo " -E, --enable - enable the given useflags"
+ echo " -D, --disable - disable the given useflags"
+ echo " -P, --prune - remove all references to the given flags from"
+ echo " make.conf to revert to default settings"
+ echo
+ echo "Notes: ${PROGRAM_NAME} currently only works for global flags defined"
+ echo " in make.globals, make.defaults or make.conf, it doesn't handle"
+ echo " use.defaults, use.mask or package.use yet (see portage(5) for details on"
+ echo " these files)."
+ echo " If multiple options are specified only the last one will be used."
}
-# 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
+showversion() {
+ echo "${PROGRAM_NAME} v${PROGRAM_VERSION}"
+ echo "Written by Marius Mauch"
+ echo
+ echo "Copyright (C) 2004 Gentoo Foundation, Inc."
+ echo "This is free software; see the source for copying conditions."
}
-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]);
- }
- }
+# the following function creates a bash array ACTIVE_FLAGS that contains the
+# global use flags, indexed by origin: 0: environment, 1: make.conf,
+# 2: make.defaults, 3: make.globals
+get_useflags() {
+ # only calculate once as calling emerge is painfully slow
+ [ -n "${USE_FLAGS_CALCULATED}" ] && return
+
+ # backup portdir so get_portdir() doesn't give false results later
+ portdir_backup="${PORTDIR}"
+
+ ACTIVE_FLAGS[0]="${USE}"
+ USE=""
+ source "${MAKE_CONF_PATH}"
+ ACTIVE_FLAGS[1]="${USE}"
+ USE=""
+ source $(get_make_defaults)
+ ACTIVE_FLAGS[2]="${USE}"
+ USE=""
+ source "${MAKE_GLOBALS_PATH}"
+ ACTIVE_FLAGS[3]="${USE}"
+
+ # restore saved env variables
+ USE="${ACTIVE_FLAGS[0]}"
+ PORTDIR="${portdir_backup}"
+
+ # get the currently active USE flags as seen by portage, this has to be after
+ # restoring USE or portage won't see the original environment
+ ACTIVE_FLAGS[9]="$(emerge --info | grep 'USE=' | cut -b 5- | sed -e 's:"::g')" #'
+ USE_FLAGS_CALCULATED=1
}
-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);
- }
- }
+# get the list of all known USE flags by reading use.desc and/or use.local.desc
+# (depending on the value of $SCOPE)
+get_useflaglist() {
+ local descdir
+
+ descdir="$(get_portdir)/profiles"
+
+ if [ -z "${SCOPE}" -o "${SCOPE}" == "global" ]; then
+ egrep "^[^# ]+ +-" "${descdir}/use.desc" | cut -d\ -f 1
+ fi
+ if [ -z "${SCOPE}" -o "${SCOPE}" == "local" ]; then
+ egrep "^[^# :]+:[^ ]+ +-" "${descdir}/use.local.desc" | cut -d: -f 2 | cut -d\ -f 1
+ fi
}
-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;
+# get the path to make.defaults by traversing the cascaded profile directories
+get_make_defaults() {
+ local curdir
+ local notfound
+ local parent
+
+ notfound=1
+ curdir="$(readlink -f ${MAKE_PROFILE_PATH})"
+
+ while [ ! -f "${curdir}/make.defaults" -a -f "${curdir}/parent" ]; do
+ parent="$(grep -v '(^#|^ *$)' ${curdir}/parent)"
+ curdir="$(readlink -f ${curdir}/${parent})"
+ done
+
+ echo "${curdir}/make.defaults"
}
-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;
- }
+# little helper function to get the status of a given flag in one of the
+# ACTIVE_FLAGS elements. Arguments are 1: flag to test, 2: index of ACTIVE_FLAGS,
+# 3: echo value for positive (and as lowercase for negative) test result,
+# 4 (optional): echo value for "missing" test result, defaults to blank
+get_flagstatus_helper() {
+ if echo " ${ACTIVE_FLAGS[${2}]} " | grep " ${1} " > /dev/null; then
+ echo -n "${3}"
+ elif echo " ${ACTIVE_FLAGS[${2}]} " | grep " -${1} " > /dev/null; then
+ echo -n "$(echo ${3} | tr [[:upper:]] [[:lower:]])"
+ else
+ echo -n "${4:- }"
+ fi
}
-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";
+# prints a status string for the given flag, each column indicating the presence
+# for portage, in the environment, in make.conf, in make.defaults and in make.globals.
+# full positive value would be "[+ECDG]", full negative value would be [-ecdg],
+# full missing value would be "[- ]" (portage only sees present or not present)
+get_flagstatus() {
+ get_useflags
+
+ echo -n '['
+ get_flagstatus_helper "${1}" 9 "+" "-"
+ get_flagstatus_helper "${1}" 0 "E"
+ get_flagstatus_helper "${1}" 1 "C"
+ get_flagstatus_helper "${1}" 2 "D"
+ get_flagstatus_helper "${1}" 3 "G"
+ echo -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";
+# faster replacement to `portageq portdir`
+get_portdir() {
+ if [ -z "${PORTDIR}" ]; then
+ use_backup="${USE}"
+ source "${MAKE_GLOBALS_PATH}"
+ source "$(get_make_defaults)"
+ source "${MAKE_CONF_PATH}"
+ USE="${use_backup}"
+ fi
+ echo "${PORTDIR}"
}
-# 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;
+# This function takes a list of use flags and shows the status and
+# the description for each one, honoring $SCOPE
+showdesc() {
+ local descdir
+ local current_desc
+ local found_one
+ local args
+
+ args="${*:-*}"
+
+ if [ -z "${SCOPE}" ]; then
+ SCOPE="global" showdesc ${args}
+ echo
+ SCOPE="local" showdesc ${args}
+ return
+ fi
+
+ descdir="$(get_portdir)/profiles"
+
+ [ "${SCOPE}" == "global" ] && echo "global use flags (searching: ${args})"
+ [ "${SCOPE}" == "local" ] && echo "local use flags (searching: ${args})"
+ echo "************************************************************"
+
+ if [ "${args}" == "*" ]; then
+ args="$(get_useflaglist | sort -u)"
+ fi
+
+ set ${args}
+
+ foundone=0
+ while [ -n "${1}" ]; do
+ if [ "${SCOPE}" == "global" ]; then
+ if grep "^${1} *-" "${descdir}/use.desc" > /dev/null; then
+ get_flagstatus "${1}"
+ foundone=1
+ fi
+ grep "^${1} *-" "${descdir}/use.desc"
+ fi
+ # local flags are a bit more complicated as there can be multiple
+ # entries per flag and we can't pipe into printf
+ if [ "${SCOPE}" == "local" ]; then
+ if grep ":${1} *-" "${descdir}/use.local.desc" > /dev/null; then
+ foundone=1
+ fi
+ grep ":${1} *-" "${descdir}/use.local.desc" \
+ | sed -e "s/^\([^:]\+\):\(${1}\) *- *\(.\+\)/\1|\2|\3/g" \
+ | while read line; do
+ pkg="$(echo $line | cut -d\| -f 1)"
+ flag="$(echo $line | cut -d\| -f 2)"
+ desc="$(echo $line | cut -d\| -f 3)"
+ get_flagstatus "${flag}"
+ printf "%s (%s):\n%s\n\n" "${flag}" "${pkg}" "${desc}"
+ done
+ fi
+ shift
+ done
+
+ if [ ${foundone} == 0 ]; then
+ echo "no matching entries found"
+ fi
}
-# 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;
+# show a list of all currently active flags and where they are activated
+showflags() {
+ local args
+
+ get_useflags
+
+ args="${*:-*}"
+
+ if [ "${args}" == "*" ]; then
+ args="$(get_useflaglist | sort -u)"
+ fi
+
+ set ${args}
+
+ while [ -n "${1}" ]; do
+ if echo " ${ACTIVE_FLAGS[9]} " | grep " ${1} " > /dev/null; then
+ printf "%-20s" ${1}
+ get_flagstatus ${1}
+ echo
+ fi
+ shift
+ done
}
-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;
+# two small helpers to add or remove a flag from a USE string
+add_flag() {
+ NEW_MAKE_CONF_USE="${NEW_MAKE_CONF_USE} ${1}"
}
-
-
-
-#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;
+remove_flag() {
+ NEW_MAKE_CONF_USE="${NEW_MAKE_CONF_USE// ${1} / }"
}
-# 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;
- }
- }
- }
+# USE flag modification function. Mainly a loop with calls to add_flag and
+# remove_flag to create a new USE string which is then inserted into make.conf.
+modify() {
+ if [ -z "${*}" ]; then
+ if [ "${ACTION}" != "prune" ]; then
+ echo "WARNING: no USE flags listed for modification, do you really"
+ echo " want to ${ACTION} *all* known USE flags?"
+ echo " If you don't please press Ctrl-C NOW!!!"
+ sleep 5
+ set $(get_useflaglist | sort -u)
+ fi
+ fi
+
+ get_useflags
+
+ NEW_MAKE_CONF_USE=" ${ACTIVE_FLAGS[1]} "
+
+ while [ -n "${1}" ]; do
+ if [ "${ACTION}" == "add" ]; then
+ if echo " ${NEW_MAKE_CONF_USE} " | grep " ${1} " > /dev/null; then
+ shift
+ elif echo " ${NEW_MAKE_CONF_USE} " | grep " -${1} " > /dev/null; then
+ remove_flag "-${1}"
+ else
+ add_flag "${1}"
+ shift
+ fi
+ elif [ "${ACTION}" == "remove" ]; then
+ if echo " ${NEW_MAKE_CONF_USE} " | grep " -${1} " > /dev/null; then
+ shift
+ elif echo " ${NEW_MAKE_CONF_USE} " | grep " ${1} " > /dev/null; then
+ remove_flag "${1}"
+ else
+ add_flag "-${1}"
+ shift
+ fi
+ elif [ "${ACTION}" == "prune" ]; then
+ if echo " ${NEW_MAKE_CONF_USE} " | grep " ${1} " > /dev/null; then
+ remove_flag "${1}"
+ elif echo " ${NEW_MAKE_CONF_USE} " | grep " -${1} " > /dev/null; then
+ remove_flag "-${1}"
+ fi
+ shift
+ fi
+ done
+
+ #echo "old flags:"
+ #echo ${ACTIVE_FLAGS[1]}
+ #echo
+ #echo "new flags:"
+ #echo ${NEW_MAKE_CONF_USE}
+
+ # a little loop to add linebreaks so we don't end with one ultra-long line
+ NEW_MAKE_CONF_USE_2=""
+ for x in ${NEW_MAKE_CONF_USE}; do
+ if [ $(((${#NEW_MAKE_CONF_USE_2}%70)+${#x}+2)) -gt 70 ]; then
+ NEW_MAKE_CONF_USE_2="${NEW_MAKE_CONF_USE_2}\\ \\n $x "
+ else
+ NEW_MAKE_CONF_USE_2="${NEW_MAKE_CONF_USE_2}${x} "
+ fi
+ done
+
+ # make a backup just in case the user doesn't like the new make.conf
+ cp -p "${MAKE_CONF_PATH}" "${MAKE_CONF_BACKUP_PATH}"
+
+ # as sed doesn't really work with multi-line patterns we have to replace USE
+ # on our own here. Basically just skip everything between USE=" and the
+ # closing ", printing our new USE line there instead.
+ inuse=0
+ (while read -r line; do
+ [ "${line:0:4}" == "USE=" ] && inuse=1
+ [ "${inuse}" == "0" ] && echo -E "${line}"
+ if [ "${inuse}" == "1" ] && echo "${line}" | egrep '" *(#.*)?$' > /dev/null; then
+ echo -n 'USE="'
+ echo -ne "${NEW_MAKE_CONF_USE_2%% }"
+ echo '"'
+ inuse=0
+ fi
+ done ) < "${MAKE_CONF_BACKUP_PATH}" | sed -e 's:\\ $:\\:' > "${MAKE_CONF_PATH}"
+
+ echo "${MAKE_CONF_PATH} was modified, a backup copy has been placed at ${MAKE_CONF_BACKUP_PATH}"
}
-# 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;
+##### main program comes now #####
+parse_arguments "$@"
+check_sanity
- $~ = '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.
-#
+eval ${MODE} ${ARGUMENTS}