summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--COPYING340
-rw-r--r--Makefile17
-rw-r--r--Portage.pm282
-rwxr-xr-xconfigure20
-rw-r--r--ufed-curses-checklist.c358
-rw-r--r--ufed-curses-help.c220
-rw-r--r--ufed-curses-help.h1
-rw-r--r--ufed-curses.c564
-rw-r--r--ufed-curses.h50
-rw-r--r--ufed.897
-rw-r--r--ufed.pl286
11 files changed, 2235 insertions, 0 deletions
diff --git a/COPYING b/COPYING
new file mode 100644
index 0000000..5b6e7c6
--- /dev/null
+++ b/COPYING
@@ -0,0 +1,340 @@
+ GNU GENERAL PUBLIC LICENSE
+ Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+ 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users. This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it. (Some other Free Software Foundation software is covered by
+the GNU Library General Public License instead.) You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+ To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have. You must make sure that they, too, receive or can get the
+source code. And you must show them these terms so they know their
+rights.
+
+ We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+ Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software. If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+ Finally, any free program is threatened constantly by software
+patents. We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary. To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ GNU GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License. The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language. (Hereinafter, translation is included without limitation in
+the term "modification".) Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+ 1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+ 2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+ a) You must cause the modified files to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ b) You must cause any work that you distribute or publish, that in
+ whole or in part contains or is derived from the Program or any
+ part thereof, to be licensed as a whole at no charge to all third
+ parties under the terms of this License.
+
+ c) If the modified program normally reads commands interactively
+ when run, you must cause it, when started running for such
+ interactive use in the most ordinary way, to print or display an
+ announcement including an appropriate copyright notice and a
+ notice that there is no warranty (or else, saying that you provide
+ a warranty) and that users may redistribute the program under
+ these conditions, and telling the user how to view a copy of this
+ License. (Exception: if the Program itself is interactive but
+ does not normally print such an announcement, your work based on
+ the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+ a) Accompany it with the complete corresponding machine-readable
+ source code, which must be distributed under the terms of Sections
+ 1 and 2 above on a medium customarily used for software interchange; or,
+
+ b) Accompany it with a written offer, valid for at least three
+ years, to give any third party, for a charge no more than your
+ cost of physically performing source distribution, a complete
+ machine-readable copy of the corresponding source code, to be
+ distributed under the terms of Sections 1 and 2 above on a medium
+ customarily used for software interchange; or,
+
+ c) Accompany it with the information you received as to the offer
+ to distribute corresponding source code. (This alternative is
+ allowed only for noncommercial distribution and only if you
+ received the program in object code or executable form with such
+ an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it. For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable. However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+ 4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License. Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+ 5. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Program or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+ 6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+ 7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all. For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+ 8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded. In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+ 9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation. If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+ 10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission. For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this. Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+ NO WARRANTY
+
+ 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+ 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+ <one line to give the program's name and a brief idea of what it does.>
+ Copyright (C) <year> <name of author>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+ Gnomovision version 69, Copyright (C) year name of author
+ Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+ This is free software, and you are welcome to redistribute it
+ under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary. Here is a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+ `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+ <signature of Ty Coon>, 1 April 1989
+ Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs. If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library. If this is what you want to do, use the GNU Library General
+Public License instead of this License.
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..8418b91
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,17 @@
+OBJ = ufed-curses.o ufed-curses-checklist.o ufed-curses-help.o
+
+ufed-curses: $(OBJ)
+ $(CC) $(LDFLAGS) -o $@ $(OBJ) -lcurses
+
+.c.o:
+ $(CC) $(CFLAGS) -D_XOPEN_SOURCE=500 -c $<
+
+clean:
+ -rm -f ufed-curses *.o
+
+ufed-curses.o: \
+ ufed-curses.c ufed-curses.h
+ufed-curses-checklist.o: \
+ ufed-curses-checklist.c ufed-curses.h ufed-curses-help.h
+ufed-curses-help.o: \
+ ufed-curses-help.c ufed-curses.h
diff --git a/Portage.pm b/Portage.pm
new file mode 100644
index 0000000..73d795f
--- /dev/null
+++ b/Portage.pm
@@ -0,0 +1,282 @@
+package Portage;
+
+# Copyright 1999-2005 Gentoo Foundation
+# Distributed under the terms of the GNU General Public License v2
+# $Header: /var/cvsroot/gentoo-src/ufed/Portage.pm,v 1.3 2005/11/13 00:28:17 truedfx Exp $
+
+my %environment;
+$environment{$_}={} for qw(USE); # INCREMENTALS, except we only need USE
+
+our %packages;
+our @profiles;
+our %use_masked_flags;
+our %use_defaults_flags;
+our %make_defaults_flags;
+our %default_flags;
+our %make_conf_flags;
+our %archs;
+our %all_flags;
+
+sub have_package($);
+sub merge(\%%);
+sub merge_env(\%);
+sub noncomments($);
+sub norm_path($$);
+sub read_archs();
+sub read_make_conf();
+sub read_make_defaults();
+sub read_make_globals();
+sub read_packages();
+sub read_profiles();
+sub read_sh($);
+sub read_use_defaults();
+sub read_use_mask();
+
+read_packages;
+read_profiles;
+read_use_mask;
+read_use_defaults;
+read_make_globals;
+read_make_defaults;
+read_make_conf;
+read_archs;
+read_use_descs;
+
+%default_flags = %make_defaults_flags;
+merge %default_flags, %use_defaults_flags;
+
+%all_flags = %default_flags;
+merge %all_flags, %make_conf_flags;
+
+for(keys %use_masked_flags)
+{ delete $all_flags{$_} if $use_masked_flags{$_} and exists $all_flags{$_} }
+
+sub have_package($) {
+ my ($cp) = @_;
+ return $packages{$cp};
+}
+
+sub merge(\%%) {
+ my ($env, %env) = @_;
+ %{$env} = () if(exists $env{'*'});
+ $env->{$_} = $env{$_} for(keys %env);
+}
+
+sub merge_env(\%) {
+ my ($env) = @_;
+ for(keys %environment) {
+ if(ref $environment{$_} eq 'HASH') {
+ if(exists $env->{$_}) {
+ my %split;
+ for(split ' ', $env->{$_}) {
+ my $off = s/^-//;
+ %split = () if($_ eq '*');
+ $split{$_} = !$off;
+ }
+ $env->{$_} = { %split };
+ merge %{$environment{$_}}, %{$env->{$_}};
+ }
+ }
+ }
+ for(keys %{$env}) {
+ if(ref $environment{$_} ne 'HASH') {
+ $environment{$_} = $env->{$_};
+ }
+ }
+}
+
+sub noncomments($) {
+ my ($fname) = @_;
+ my @result;
+ local $/;
+ if(open my $file, '<', $fname) {
+ @result = split /(?:[^\S\n]*(?:#.*)?\n)+/, <$file>."\n";
+ shift @result if @result>0 && $result[0] eq '';
+ close $file;
+ }
+ return @result;
+}
+
+sub norm_path($$) {
+ my ($base, $path) = @_;
+ my @pathcomp = ($path !~ m!^/! && split(m!/!, $base), split(m!/!, $path));
+ for(my $i=0;;$i++) {
+ last if $i == @pathcomp; # don't want to skip this with redo
+ if($pathcomp[$i] eq '' || $pathcomp[$i] eq '.') {
+ splice @pathcomp, $i, 1;
+ redo;
+ }
+ if($pathcomp[$i] eq '..') {
+ if($i==0) {
+ splice @pathcomp, 0, 1;
+ } else {
+ splice @pathcomp, --$i, 2;
+ }
+ redo;
+ }
+ }
+ return '/'.join '/', @pathcomp;
+}
+
+sub read_archs() {
+ for my $dir(@portagedirs) {
+ for(noncomments "$dir/profiles/arch.list") {
+ $archs{$_} = 1;
+ }
+ }
+}
+
+sub read_make_conf() {
+ my %env = read_sh "/etc/make.conf";
+ merge %make_conf_flags, %{$env{USE}} if exists $env{USE};
+ @portagedirs = $environment{PORTDIR};
+ push @portagedirs, split ' ', $environment{PORTDIR_OVERLAY} if defined $environment{PORTDIR_OVERLAY};
+}
+
+sub read_make_defaults() {
+ for my $dir(@profiles) {
+ my %env = read_sh "$dir/make.defaults";
+ merge %make_defaults_flags, %{$env{USE}} if exists $env{USE};
+ }
+}
+
+sub read_make_globals() {
+ for my $dir(@profiles, '/etc') {
+ read_sh "$dir/make.globals";
+ }
+}
+
+sub read_packages() {
+ die "Couldn't read /var/db/pkg\n" unless opendir my $pkgdir, '/var/db/pkg';
+ while(my $cat = readdir $pkgdir) {
+ next if $cat eq '.' or $cat eq '..';
+ next unless opendir my $catdir, "/var/db/pkg/$cat";
+ while(my $pkg = readdir $catdir) {
+ next if $pkg eq '.' or $pkg eq '..';
+ if(open my $provide, '<', "/var/db/pkg/$cat/$pkg/PROVIDE") {
+ if(open my $use, '<', "/var/db/pkg/$cat/$pkg/USE") {
+ # could be shortened, but make sure not to strip off part of the name
+ $pkg =~ s/-\d+(?:\.\d+)*\w?(?:_(?:alpha|beta|pre|rc|p)\d*)?(?:-r\d+)?$//;
+ $packages{"$cat/$pkg"} = 1;
+ local $/;
+ my @provide = split ' ', <$provide>;
+ my @use = split ' ', <$use>;
+ for(my $i=0; $i<@provide; $i++) {
+ my $pkg = $provide[$i];
+ next if $pkg eq '(' || $pkg eq ')';
+ if($pkg !~ s/\?$//) {
+ $pkg =~ s/-\d+(?:\.\d+)*\w?(?:_(?:alpha|beta|pre|rc|p)\d*)?(?:-r\d+)?$//;
+ $packages{$pkg} = 1;
+ } else {
+ my $musthave = $pkg !~ s/^!//;
+ my $have = 0;
+ for(@use) {
+ if($pkg eq $_)
+ { $have = 1; last }
+ }
+ if($musthave != $have) {
+ my $level = 0;
+ for($i++;$i<@provide;$i++) {
+ $level++ if $provide[$i] eq '(';
+ $level-- if $provide[$i] eq ')';
+ last if $level==0;
+ }
+ }
+ }
+ }
+ close $use;
+ }
+ close $provide;
+ }
+ }
+ closedir $catdir;
+ }
+ closedir $pkgdir;
+}
+
+sub read_profiles() {
+ $_ = readlink '/etc/make.profile';
+ die "/etc/make.profile is not a symlink\n" if not defined $_;
+ @profiles = norm_path '/etc', $_;
+ PARENT: {
+ for(noncomments "$profiles[0]/parent") {
+ unshift @profiles, norm_path $profiles[0], $_;
+ redo PARENT;
+ }
+ }
+}
+
+sub read_sh($) {
+ my $BLANK = qr{(?:[ \n\t]+|#.*)+}; # whitespace and comments
+ my $IDENT = qr{([^ \\\n\t'"{}=]+)}; # identifiers
+ my $ASSIG = qr{=}; # assignment operator
+ my $UQVAL = qr{((?:[^ \\\n\t'"]+|\\.)+)}s; # unquoted value
+ my $SQVAL = qr{'([^']*)'}; # singlequoted value
+ my $DQVAL = qr{"((?:[^\\"]|\\.)*)"}s; # doublequoted value
+
+ my ($fname) = @_;
+ my %env;
+ if(open my $file, '<', $fname) {
+ { local $/; $_ = <$file> }
+ eval {
+ for(;;) {
+ /\G$BLANK/gc;
+ last if pos == length;
+ /\G$IDENT/gc or die;
+ my $name = $1;
+ /\G$BLANK/gc;
+ /\G$ASSIG/gc or die;
+ /\G$BLANK/gc;
+ die if pos == length;
+ my $value = '';
+ for(;;) {
+ if(/\G$UQVAL/gc || /\G$DQVAL/gc) {
+ my $addvalue = $1;
+ $addvalue =~ s[
+ \\\n | # backslash-newline
+ \\(.) | # other escaped characters
+ \$({)? # $
+ $IDENT # followed by an identifier
+ (?(2)}) # optionally enclosed in braces
+ ][
+ defined $3 ? $env{$3} || '' : # replace envvars
+ defined $1 ? $1 : # unescape escaped characters
+ '' # delete backslash-newlines
+ ]gex;
+ $value .= $addvalue
+ } elsif(/\G$SQVAL/gc) {
+ $value .= $1
+ } else {
+ last
+ }
+ }
+ $env{$name} = $value;
+ }
+ };
+ die "Parse error in $fname\n" if $@;
+ close $file;
+ }
+ merge_env %env;
+ return %env if wantarray;
+}
+
+sub read_use_defaults() {
+ for my $dir(@profiles) {
+ for(noncomments "$dir/use.defaults") {
+ my ($flag, @packages) = split;
+ for(@packages)
+ { $use_defaults_flags{$flag} = 1 if have_package $_ }
+ }
+ }
+}
+
+sub read_use_mask() {
+ for my $dir(@profiles) {
+ for(noncomments "$dir/use.mask") {
+ my $off = s/^-//;
+ $use_masked_flags{$_} = !$off;
+ }
+ }
+}
+
+1;
diff --git a/configure b/configure
new file mode 100755
index 0000000..5a0847d
--- /dev/null
+++ b/configure
@@ -0,0 +1,20 @@
+#!/bin/sh
+CC="${CC-cc}"
+case "$(${CC} -v 2>&1)" in
+*"gcc version 2."*)
+ CFLAGS="-ansi -pedantic -Wall -W ";;
+*"gcc version 3."[0-3]*)
+ CFLAGS="-std=c99 -pedantic -Wall -W ";;
+*"gcc version"*)
+ CFLAGS="-std=c99 -pedantic -Wall -Wextra ";;
+*)
+ case "$(${CC} --version 2>&1)" in
+ *"(ICC)"*)
+ # 981: unspecified order of evaluation
+ CFLAGS="-c99 -strict-ansi -Wall -wd981 ";;
+ *)
+ CFLAGS="";;
+ esac
+esac
+sed -e "s/\$(CFLAGS).*-D/\$(CFLAGS) ${CFLAGS}-D/" Makefile > Makefile.tmp &&
+mv -f Makefile.tmp Makefile
diff --git a/ufed-curses-checklist.c b/ufed-curses-checklist.c
new file mode 100644
index 0000000..eac0d4a
--- /dev/null
+++ b/ufed-curses-checklist.c
@@ -0,0 +1,358 @@
+#include "ufed-curses.h"
+
+#include <ctype.h>
+#include <errno.h>
+#include <limits.h>
+#include <locale.h>
+#include <stdlib.h>
+#include <string.h>
+#include <strings.h>
+#include <unistd.h>
+
+#include "ufed-curses-help.h"
+
+static char *getline(FILE *fp) {
+ size_t size;
+ char *result;
+
+ size = LINE_MAX;
+ result = malloc(size);
+ if(result==NULL)
+ exit(-1);
+ if(fgets(result, size, fp)==NULL)
+ return NULL;
+ {
+ char *p = strchr(result, '\n');
+ if(p!=NULL) {
+ *p++ = '\0';
+ p = realloc(result, p-result);
+ return p ? p : result;
+ }
+ }
+ for(;;) {
+ result = realloc(result, size+size/2);
+ if(result==NULL)
+ exit(-1);
+ if(fgets(result+size, size/2, fp)==NULL)
+ return NULL;
+ {
+ char *p = strchr(result+size, '\n');
+ if(p!=NULL) {
+ *p++ = '\0';
+ p = realloc(result, p-result);
+ return p ? p : result;
+ }
+ }
+ size += size/2;
+ }
+}
+
+static struct flag {
+ struct item item;
+ char *name;
+ char on;
+ char *state;
+#if C99
+ char *descr[];
+#else
+ __extension__ char *descr[0];
+#endif
+} *flags;
+static int descriptionleft;
+
+static void free_flags(void);
+static void read_flags(void) {
+ FILE *input = fdopen(3, "r");
+ char *line;
+ int y=0;
+ if(input==NULL)
+ exit(-1);
+ atexit(&free_flags);
+ for(;;) {
+ struct {
+ int start, end;
+ } name, on, state;
+ int ndescr;
+ struct flag *flag;
+ line = getline(input);
+ if(line==NULL)
+ break;
+ if(sscanf(line, "%n%*s%n %n%*s%n %n(%*[ +-])%n %d",
+ &name.start, &name.end,
+ &on.start, &on.end,
+ &state.start, &state.end,
+ &ndescr)!=1)
+ exit(-1);
+ flag = malloc(sizeof *flag + ndescr * sizeof *flag->descr);
+ if(flag==NULL)
+ exit(-1);
+ flag->item.top = y;
+
+ line[name.end] = '\0';
+ if(name.end-name.start+12 > minwidth)
+ minwidth = name.end-name.start+12;
+ flag->name = &line[name.start];
+
+ line[on.end] = '\0';
+ if(!strcmp(&line[on.start], "on"))
+ flag->on = 'X';
+ else if(!strcmp(&line[on.start], "off"))
+ flag->on = ' ';
+ else
+ exit(-1);
+
+ line[state.end] = '\0';
+ if(state.end-state.start != 5)
+ exit(-1);
+ flag->state = &line[state.start];
+
+ flag->item.height = ndescr;
+ if(ndescr > minheight)
+ minheight = ndescr;
+ { int i; for(i=0; i<ndescr; i++) {
+ flag->descr[i] = getline(input);
+ } }
+
+ y += ndescr;
+
+ if(flags==NULL) {
+ flag->item.prev = (struct item *) flag;
+ flag->item.next = (struct item *) flag;
+ flags = flag;
+ } else {
+ flag->item.next = (struct item *) flags;
+ flag->item.prev = flags->item.prev;
+ flags->item.prev->next = (struct item *) flag;
+ flags->item.prev = (struct item *) flag;
+ }
+ }
+ fclose(input);
+ if(flags==NULL) {
+ fputs("No input!\n", stderr);
+ exit(-1);
+ }
+}
+
+static void free_flags(void) {
+ struct flag *flag = flags;
+ if(flag!=NULL) {
+ flag->item.prev->next = NULL;
+ do {
+ void *p = flag;
+ char **descr;
+ for(descr=&flag->descr[0]; descr!=&flag->descr[flag->item.height]; descr++)
+ free(*descr);
+ flag = (struct flag *) flag->item.next;
+ free(p);
+ } while(flag!=NULL);
+ flags = NULL;
+ }
+}
+
+static const struct key keys[] = {
+#define key(x) x, sizeof(x)-1
+ { '?', key("Help (?)") },
+ { '\n', key("Save (Return/Enter)") },
+ { '\033', key("Cancel (Esc)") },
+ { '\0', key("") }
+#undef key
+};
+
+static void drawflag(struct item *item, bool highlight) {
+ struct flag *flag = (struct flag *) item;
+#if C99
+ char buf[wWidth(List)+1];
+#else
+ char *buf = __builtin_alloca(wWidth(List)+1);
+#endif
+ char **d;
+
+ int y = flag->item.top - topy;
+ if(!highlight)
+ wattrset(win(List), COLOR_PAIR(3));
+ else
+ wattrset(win(List), COLOR_PAIR(3) | A_BOLD | A_REVERSE);
+ if(y < 0) {
+ wmove(win(List), 0, 0);
+ d = &flag->descr[-y];
+ y = 0;
+ goto descriptiononly;
+ }
+ wmove(win(List), y, 0);
+ sprintf(buf, " [%c] %-*s %-5.5s ",
+ flag->on,
+ minwidth-12, flag->name,
+ flag->state);
+ d = &flag->descr[0];
+ if(d != &flag->descr[flag->item.height]) {
+ for(;;) {
+ sprintf(buf+minwidth, "%-*.*s",
+ wWidth(List)-minwidth, wWidth(List)-minwidth,
+ strlen(*d) > (size_t)descriptionleft
+ ? &(*d)[descriptionleft]
+ : "");
+ waddstr(win(List), buf);
+ d++;
+ y++;
+ descriptiononly:
+ if(d!=&flag->descr[flag->item.height] && y<wHeight(List)) {
+ char *p;
+ for(p=buf; p!=buf+minwidth; p++)
+ *p = ' ';
+ continue;
+ }
+ break;
+ }
+ } else {
+ memset(buf+minwidth, ' ', wWidth(List)-minwidth);
+ buf[wWidth(List)] = '\0';
+ waddstr(win(List), buf);
+ y++;
+ }
+ if(highlight)
+ wmove(win(List), flag->item.top - topy, 2);
+ wnoutrefresh(win(List));
+}
+
+static char *fayt;
+static struct item **faytsave;
+
+static int callback(struct item **currentitem, int key) {
+ if(*fayt!='\0' && key!=KEY_BACKSPACE && (key==' ' || key!=(unsigned char) key || !isprint(key))) {
+ *fayt = '\0';
+ wattrset(win(Input), COLOR_PAIR(3));
+ mvwhline(win(Input), 0, 0, ' ', wWidth(Input));
+ wrefresh(win(Input));
+ }
+ if(descriptionleft!=0 && key!=KEY_LEFT && key!=KEY_RIGHT) {
+ descriptionleft = 0;
+ drawflag(*currentitem, TRUE);
+ }
+ switch(key) {
+ default:
+ if(key==(unsigned char) key && isprint(key)) {
+ struct item *item = *currentitem;
+ int n = strlen(fayt);
+ if(strncasecmp(((struct flag *) item)->name, fayt, n)!=0)
+ n--;
+ fayt[n] = (char) key;
+ faytsave[n] = *currentitem;
+ n++;
+ fayt[n] = '\0';
+ if(strncasecmp(((struct flag *) item)->name, fayt, n)==0) {
+ wattrset(win(Input), COLOR_PAIR(3) | A_BOLD);
+ mvwaddstr(win(Input), 0, 0, fayt);
+ wrefresh(win(Input));
+ } else {
+ do item = item->next;
+ while(item!=*currentitem && strncasecmp(((struct flag *) item)->name, fayt, n)!=0);
+ if(item==*currentitem) {
+ wattrset(win(Input), COLOR_PAIR(4) | A_BOLD | A_REVERSE);
+ mvwaddstr(win(Input), 0, 0, fayt);
+ wmove(win(Input), 0, n-1);
+ wnoutrefresh(win(List));
+ wrefresh(win(Input));
+ } else {
+ drawflag(*currentitem, FALSE);
+ *currentitem = item;
+ scrollcurrent();
+ drawflag(*currentitem, TRUE);
+ wattrset(win(Input), COLOR_PAIR(3) | A_BOLD);
+ mvwaddstr(win(Input), 0, 0, fayt);
+ wnoutrefresh(win(List));
+ wrefresh(win(Input));
+ }
+ }
+ }
+ break;
+ case KEY_BACKSPACE: {
+ int n = strlen(fayt);
+ if(n==0)
+ break;
+ n--;
+ fayt[n] = '\0';
+ drawflag(*currentitem, FALSE);
+ *currentitem = faytsave[n];
+ scrollcurrent();
+ drawflag(*currentitem, TRUE);
+ wattrset(win(Input), COLOR_PAIR(3) | A_BOLD);
+ mvwaddstr(win(Input), 0, 0, fayt);
+ whline(win(Input), ' ', 2);
+ if(n==0) {
+ wmove(win(List), (*currentitem)->top-topy, 2);
+ wnoutrefresh(win(Input));
+ wrefresh(win(List));
+ } else {
+ wnoutrefresh(win(List));
+ wrefresh(win(Input));
+ }
+ break;
+ }
+ case '\n':
+ case KEY_ENTER:
+ if(yesno("Save and exit? (Y/N) "))
+ return 0;
+ break;
+ case '\033':
+ if(yesno("Cancel? (Y/N) "))
+ return 1;
+ break;
+ case ' ':
+ ((struct flag *) *currentitem)->on ^= ' '^'X';
+ wattrset(win(List), COLOR_PAIR(3) | A_BOLD | A_REVERSE);
+ mvwhline(win(List), (*currentitem)->top-topy, 2, ((struct flag *) *currentitem)->on, 1);
+ wrefresh(win(List));
+ break;
+ case KEY_LEFT:
+ if(descriptionleft>0)
+ descriptionleft--;
+ drawflag(*currentitem, TRUE);
+ wmove(win(List), (*currentitem)->top-topy, 2);
+ wrefresh(win(List));
+ break;
+ case KEY_RIGHT:
+ descriptionleft++;
+ drawflag(*currentitem, TRUE);
+ wmove(win(List), (*currentitem)->top-topy, 2);
+ wrefresh(win(List));
+ break;
+#ifdef NCURSES_MOUSE_VERSION
+ case KEY_MOUSE:
+ ((struct flag *) *currentitem)->on ^= ' '^'X';
+ break;
+#endif
+ case '?':
+ help();
+ break;
+ }
+ return -1;
+}
+
+int main(void) {
+ int result;
+
+ read_flags();
+ fayt = malloc((minwidth-12+2) * sizeof *fayt);
+ faytsave = malloc((minwidth-12+2) * sizeof *faytsave);
+ if(fayt==NULL || faytsave==NULL)
+ exit(-1);
+ fayt[0] = '\0';
+
+ initcurses();
+ result=maineventloop("Select desired USE flags from the list below:",
+ &callback, &drawflag, (struct item *) flags, keys);
+ cursesdone();
+
+ if(result==0) {
+ FILE *output = fdopen(4, "w");
+ struct flag *flag = flags;
+ do {
+ if(flag->on=='X')
+ fprintf(output, "%s\n", flag->name);
+ flag = (struct flag *) flag->item.next;
+ } while(flag!=flags);
+ fclose(output);
+ }
+
+ return result;
+}
diff --git a/ufed-curses-help.c b/ufed-curses-help.c
new file mode 100644
index 0000000..dd0d2cf
--- /dev/null
+++ b/ufed-curses-help.c
@@ -0,0 +1,220 @@
+#include "ufed-curses-help.h"
+
+#include "ufed-curses.h"
+
+#include <ctype.h>
+#include <errno.h>
+#include <limits.h>
+#include <locale.h>
+#include <stdlib.h>
+#include <string.h>
+#include <strings.h>
+#include <unistd.h>
+
+static struct line {
+ struct item item;
+ char *text;
+} *lines;
+static int helpheight, helpwidth;
+
+static void free_lines(void);
+static void init_lines(void) {
+ static const char * const help[] = {
+"ufed is a simple program designed to help you configure the systems USE "
+"flags (see below) to your liking. Use the Up and Down arrow keys, the "
+"Page Up and Page Down keys, the Home and End keys, or start typing the "
+"name of a flag to select it. Then, use the space bar to toggle the "
+"setting. After changing flags, press the Return or Enter key to make "
+"this permanent, or press Escape to revert your changes.",
+"",
+"Note: Depending on your system, you may need to wait a second before "
+"ufed detects this key; in some cases, you can use the ncurses "
+"environment variable ESCDELAY to change this. See the ncurses(3x) "
+"manpage for more info.",
+"",
+"ufed will present you with a list of descriptions for each USE flag. If "
+"a description is too long to fit on your screen, you can use the Left "
+"and Right arrow keys to scroll the descriptions.",
+"",
+"ufed attempts to show you where a particular use setting came from. "
+"Each USE flag has a 3 character descriptor that represents the three "
+"ways a use flag can be set.",
+"",
+"The 1st char is the setting from the /etc/make.profile/make.defaults "
+"file. These are the defaults for Gentoo as a whole. These should not be "
+"changed.",
+"",
+"The 2nd char is the setting from the /etc/make.profile/use.defaults "
+"file. These will change as packages are added and removes from the "
+"system.",
+"",
+"The 3rd char is the settings from the /etc/make.conf file. these are "
+"the only ones that should be changed by the user and these are the ones "
+"that ufed changes.",
+"",
+"If the character is a + then that USE flag was set in that file, if it "
+"is a space then the flag was not mentioned in that file and if it is a "
+"- then that flag was unset in that file.",
+"",
+"-- What Are USE Flags? --",
+"",
+"The USE settings system is a flexible way to enable or disable various "
+"features at package build-time on a global level and for individual "
+"packages. This allows an administrator control over how packages are "
+"built in regards to the optional features which can be compiled into "
+"those packages.",
+"",
+"For instance, packages with optional GNOME support can have this "
+"support disabled at compile time by disabling the \"gnome\" USE setting. "
+"Enabling the \"gnome\" USE setting would enable GNOME support in these "
+"same packages.",
+"",
+"The effect of USE settings on packages is dependent on whether both the "
+"software itself and the package ebuild supports the USE setting as an "
+"optional feature. If the software does not have support for an optional "
+"feature then the corresponding USE setting will obviously have no "
+"effect.",
+"",
+"Also many package dependencies are not considered optional by the "
+"software and thus USE settings will have no effect on those mandatory "
+"dependencies.",
+"",
+"A list of USE keywords used by a particular package can be found by "
+"checking the IUSE line in any ebuild file.",
+"",
+"See",
+" http://www.gentoo.org/doc/en/handbook/handbook-x86.xml?part=2&chap=1",
+"for more information on USE flags.",
+"",
+"Please also note that if ufed describes a flag as (Unknown) it "
+"generally means that it is either a spelling error in one of the three "
+"configuration files or it is not an offically sanctioned USE flag. "
+"Sanctioned USE flags can be found in",
+" /usr/portage/profiles/use.desc",
+"and in",
+" /usr/portage/profiles/use.local.desc",
+"",
+"***",
+"",
+"ufed was originally written by Maik Schreiber <blizzy@blizzy.de>.",
+"ufed was previously maintained by Robin Johnson <robbat2@gentoo.org>, "
+"Fred Van Andel <fava@gentoo.org>, and Arun Bhanu <codebear@gentoo.org>.",
+"ufed is currently maintained by Harald van Dijk <truedfx@gentoo.org>.",
+"",
+"Copyright 1999-2005 Gentoo Foundation",
+"Distributed under the terms of the GNU General Public License v2"
+ };
+ struct line *line;
+ const char * const *paragraph = &help[0], *word = &help[0][0];
+ int n, y=0;
+
+ helpheight = wHeight(List);
+ helpwidth = wWidth(List);
+
+ atexit(&free_lines);
+ for(;;) {
+ line = malloc(sizeof *line);
+ if(line==NULL)
+ exit(-1);
+ if(lines==NULL) {
+ line->item.prev = (struct item *) line;
+ line->item.next = (struct item *) line;
+ lines = line;
+ } else {
+ line->item.next = (struct item *) lines;
+ line->item.prev = lines->item.prev;
+ lines->item.prev->next = (struct item *) line;
+ lines->item.prev = (struct item *) line;
+ }
+ line->item.top = y++;
+ line->item.height = 1;
+ n = strlen(word);
+ if(n > helpwidth-1) {
+ for(n = helpwidth-1; word[n]!=' '; n--) {
+ if(n==0) {
+ n = helpwidth;
+ break;
+ }
+ }
+ }
+ line->text = malloc(n+1);
+ if(line->text==NULL)
+ exit(-1);
+ memcpy(line->text, word, n);
+ line->text[n] = '\0';
+ while(word[n]==' ')
+ n++;
+ word += n;
+ if(word[0]=='\0') {
+ paragraph++;
+ if(paragraph == &help[sizeof help / sizeof *help])
+ break;
+ word = &(*paragraph)[0];
+ }
+ }
+}
+
+static void free_lines(void) {
+ struct line *line = lines;
+ if(line!=NULL) {
+ line->item.prev->next = NULL;
+ do {
+ void *p = line;
+ free(line->text);
+ line = (struct line *) line->item.next;
+ free(p);
+ } while(line!=NULL);
+ lines = NULL;
+ }
+}
+
+static const struct key keys[] = {
+#define key(x) x, sizeof(x)-1
+ { '\033', key("Back (Esc)") },
+ { '\0', key("") }
+#undef key
+};
+
+static void drawline(struct item *item, bool highlight) {
+ struct line *line = (struct line *) item;
+#if C99
+ char buf[wWidth(List)+1];
+#else
+ char *buf = __builtin_alloca(wWidth(List)+1);
+#endif
+ sprintf(buf, "%-*.*s", wWidth(List), wWidth(List), line->text);
+ if(!highlight)
+ wattrset(win(List), COLOR_PAIR(3));
+ else
+ wattrset(win(List), COLOR_PAIR(3) | A_BOLD | A_REVERSE);
+ mvwaddstr(win(List), line->item.top-topy, 0, buf);
+ if(highlight)
+ wmove(win(List), line->item.top-topy, 0);
+ wnoutrefresh(win(List));
+}
+
+static int callback(struct item **currentitem, int key) {
+ switch(key) {
+ case 'Q': case 'q':
+ case '\033':
+ return 0;
+#ifdef KEY_RESIZE
+ case KEY_RESIZE:
+ free_lines();
+ init_lines();
+ *currentitem = (struct item *) lines;
+ return -2;
+#endif
+ default:
+ return -1;
+ }
+}
+
+void help(void) {
+ if(helpheight!=wHeight(List) || helpwidth!=wWidth(List)) {
+ if(lines!=NULL)
+ free_lines();
+ init_lines();
+ }
+ maineventloop("", &callback, &drawline, (struct item *) lines, keys);
+}
diff --git a/ufed-curses-help.h b/ufed-curses-help.h
new file mode 100644
index 0000000..5c66482
--- /dev/null
+++ b/ufed-curses-help.h
@@ -0,0 +1 @@
+extern void help(void);
diff --git a/ufed-curses.c b/ufed-curses.c
new file mode 100644
index 0000000..f2af8e9
--- /dev/null
+++ b/ufed-curses.c
@@ -0,0 +1,564 @@
+#include "ufed-curses.h"
+
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <locale.h>
+
+struct window window[wCount] = {
+ { NULL, 0, 0, 5, 0 }, /* Top */
+ { NULL, 5, 0, -8, 3 }, /* Left */
+ { NULL, 5, 3, -9, -6 }, /* List */
+ { NULL, -4, 3, 1, -6 }, /* Input */
+ { NULL, 5, -3, -8, 1 }, /* Scrollbar */
+ { NULL, 5, -2, -8, 2 }, /* Right */
+ { NULL, -3, 0, 3, 0 }, /* Bottom */
+};
+
+static const char *subtitle;
+
+static const struct key *keys;
+
+static struct item *items, *currentitem;
+int topy, minheight, minwidth;
+
+static void checktermsize(void);
+
+void initcurses(void) {
+ setlocale(LC_CTYPE, "");
+ initscr();
+ start_color();
+ cbreak();
+ noecho();
+ keypad(stdscr, TRUE);
+ init_pair(1, COLOR_CYAN, COLOR_BLUE);
+ init_pair(2, COLOR_WHITE, COLOR_WHITE);
+ init_pair(3, COLOR_BLACK, COLOR_WHITE);
+ init_pair(4, COLOR_RED, COLOR_WHITE);
+#ifdef NCURSES_MOUSE_VERSION
+ mousemask(BUTTON1_CLICKED | BUTTON1_DOUBLE_CLICKED | BUTTON1_PRESSED | BUTTON1_RELEASED, NULL);
+#endif
+ checktermsize();
+ { enum win w; for(w = (enum win) 0; w != wCount; w++) {
+ window[w].win = newwin(wHeight(w), wWidth(w), wTop(w), wLeft(w));
+ } }
+}
+
+void cursesdone(void) {
+ enum win w;
+ for(w = (enum win) 0; w != wCount; w++)
+ delwin(window[w].win);
+ endwin();
+}
+
+static void checktermsize(void) {
+ while(wHeight(List) < minheight
+ || wWidth(List) < minwidth) {
+#ifdef KEY_RESIZE
+ clear();
+ attrset(0);
+ mvaddstr(0, 0, "Your screen is too small. Press Ctrl+C to exit.");
+ while(getch()!=KEY_RESIZE) {}
+#else
+ cursesdone();
+ fputs("Your screen is too small.\n", stderr);
+ exit(-1);
+#endif
+ }
+}
+
+static void (*drawitem)(struct item *, bool);
+
+static void drawitems(void) {
+ struct item *item;
+ int y;
+
+ item = currentitem;
+ while((y=item->top-topy) > 0)
+ item = item->prev;
+
+ for(;;) {
+ if(item!=currentitem)
+ (*drawitem)(item, FALSE);
+ y += item->height;
+ item = item->next;
+ if(y>=wHeight(List))
+ break;
+ if(item==items) {
+#if C99
+ char buf[wWidth(List)];
+#else
+ char *buf = __builtin_alloca(wWidth(List));
+#endif
+ memset(buf, ' ', wWidth(List));
+ buf[wWidth(List)] = '\0';
+ wmove(win(List), y, 0);
+ wattrset(win(List), COLOR_PAIR(3));
+ while(y++ < wHeight(List))
+ waddstr(win(List), buf);
+ break;
+ }
+ }
+ (*drawitem)(currentitem, TRUE);
+ wnoutrefresh(win(List));
+}
+
+static void drawscrollbar(void) {
+ WINDOW *w = win(Scrollbar);
+ wattrset(w, COLOR_PAIR(3) | A_BOLD);
+ mvwaddch(w, 0, 0, ACS_UARROW);
+ wvline(w, ACS_CKBOARD, wHeight(Scrollbar)-3);
+ mvwaddch(w, 1+(wHeight(Scrollbar)-3)*topy/(items->prev->top+items->prev->height-(wHeight(List)-1)), 0, ACS_BLOCK);
+ mvwaddch(w, wHeight(Scrollbar)-2, 0, ACS_DARROW);
+ mvwaddch(w, wHeight(Scrollbar)-1, 0, ACS_VLINE);
+ wnoutrefresh(w);
+}
+
+static void draw(void) {
+#if C99
+ char buf[COLS+1];
+#else
+ char *buf = __builtin_alloca(COLS+1);
+#endif
+ WINDOW *w;
+
+ wnoutrefresh(stdscr);
+
+ w = win(Top);
+
+ wattrset(w, COLOR_PAIR(1) | A_BOLD);
+ sprintf(buf, "%-*.*s", wWidth(Top), wWidth(Top), "Gentoo USE flags editor 0.40");
+ mvwaddstr(w, 0, 0, buf);
+
+ whline(w, ACS_HLINE, wWidth(Top));
+
+ wattrset(w, COLOR_PAIR(2) | A_BOLD);
+ mvwaddch(w, 2, 0, ACS_ULCORNER);
+ whline(w, ACS_HLINE, wWidth(Top)-2);
+ mvwaddch(w, 2, wWidth(Top)-1, ACS_URCORNER);
+
+ waddch(w, ACS_VLINE);
+ wattrset(w, COLOR_PAIR(3));
+ sprintf(buf, " %-*.*s ", wWidth(Top)-4, wWidth(Top)-4, subtitle);
+ waddstr(w, buf);
+ wattrset(w, COLOR_PAIR(2) | A_BOLD);
+ waddch(w, ACS_VLINE);
+
+ /* maybe this should be based on List? */
+ waddch(w, ACS_VLINE);
+ wattrset(w, COLOR_PAIR(3));
+ waddch(w, ' ');
+ waddch(w, ACS_ULCORNER);
+ whline(w, ACS_HLINE, wWidth(Top)-6);
+ mvwaddch(w, 4, wWidth(Top)-3, ACS_URCORNER);
+ waddch(w, ' ');
+ wattrset(w, COLOR_PAIR(2) | A_BOLD);
+ waddch(w, ACS_VLINE);
+
+ wnoutrefresh(w);
+
+ w = win(Left);
+ wattrset(w, COLOR_PAIR(2) | A_BOLD);
+ mvwvline(w, 0, 0, ACS_VLINE, wHeight(Left));
+ wattrset(w, COLOR_PAIR(3));
+ mvwvline(w, 0, 1, ' ', wHeight(Left));
+ mvwvline(w, 0, 2, ACS_VLINE, wHeight(Left));
+ wnoutrefresh(w);
+
+ w = win(Right);
+ wattrset(w, COLOR_PAIR(2) | A_BOLD);
+ mvwvline(w, 0, 0, ' ', wHeight(Right));
+ mvwvline(w, 0, 1, ACS_VLINE, wHeight(Right));
+ wnoutrefresh(w);
+
+ w = win(Bottom);
+
+ wattrset(w, COLOR_PAIR(2) | A_BOLD);
+ mvwaddch(w, 0, 0, ACS_VLINE);
+ wattrset(w, COLOR_PAIR(3));
+ waddch(w, ' ');
+ waddch(w, ACS_LLCORNER);
+ whline(w, ACS_HLINE, wWidth(Bottom)-6);
+ mvwaddch(w, 0, wWidth(Bottom)-3, ACS_LRCORNER);
+ waddch(w, ' ');
+ wattrset(w, COLOR_PAIR(2) | A_BOLD);
+ waddch(w, ACS_VLINE);
+
+ waddch(w, ACS_VLINE);
+ wattrset(w, COLOR_PAIR(3));
+ {
+ char *p = buf;
+ const struct key *key;
+ *p++ = ' ';
+ for(key=keys; key->key!='\0'; key++) {
+ int n = (buf+wWidth(Bottom)-3) - p;
+ if(n > key->length)
+ n = key->length;
+ memcpy(p, key->descr, n);
+ p += n;
+ *p++ = ' ';
+ if(p == buf+wWidth(Bottom)-3)
+ break;
+ }
+ memset(p, ' ', buf+wWidth(Bottom)-2-p);
+ buf[wWidth(Bottom)-2] = '\0';
+ }
+ waddstr(w, buf);
+ wattrset(w, COLOR_PAIR(2) | A_BOLD);
+ waddch(w, ACS_VLINE);
+
+ waddch(w, ACS_LLCORNER);
+ whline(w, ACS_HLINE, wWidth(Bottom)-2);
+ mvwhline(w, 2, wWidth(Bottom)-1, ACS_LRCORNER, 1);
+
+ wnoutrefresh(w);
+
+ w = win(Input);
+ wattrset(w, COLOR_PAIR(3));
+ mvwhline(w, 0, 0, ' ', wWidth(Input));
+ wnoutrefresh(w);
+
+ drawitems();
+ drawscrollbar();
+
+ wrefresh(win(List));
+}
+
+void scrollcurrent(void) {
+ int oldtopy = topy;
+ if(currentitem->top < topy)
+ topy = currentitem->top;
+ else if(currentitem->top+currentitem->height > topy+wHeight(List))
+ topy = currentitem->top+currentitem->height-wHeight(List);
+ else
+ return;
+ if(abs(topy-oldtopy)>wHeight(List)) {
+ drawitems();
+ } else {
+ struct item *item = currentitem;
+ scrollok(win(List), TRUE);
+ wscrl(win(List), topy-oldtopy);
+ scrollok(win(List), FALSE);
+ if(topy<oldtopy)
+ while((item=item->next)!=items
+ && item->top < oldtopy
+ && item->top < topy + wHeight(List))
+ (*drawitem)(item, FALSE);
+ else
+ while((item=item->prev)->next!=items
+ && item->top > oldtopy
+ && item->top + item->height-1 >= topy)
+ (*drawitem)(item, FALSE);
+ mvwhline(win(List), wHeight(List), 0, ' ', wWidth(List));
+ }
+ drawscrollbar();
+}
+
+bool yesno(const char *prompt) {
+ wattrset(win(Input), COLOR_PAIR(4) | A_BOLD | A_REVERSE);
+ mvwhline(win(Input), 0, 0, ' ', wWidth(Input));
+ waddstr(win(Input), prompt);
+ whline(win(Input), 'Y', 1);
+ wrefresh(win(Input));
+ for(;;) switch(getch()) {
+ case '\n': case KEY_ENTER:
+ case 'Y': case 'y':
+ return TRUE;
+ case '\033':
+ case 'N': case 'n':
+ wattrset(win(Input), COLOR_PAIR(3));
+ mvwhline(win(Input), 0, 0, ' ', wWidth(Input));
+ wnoutrefresh(win(Input));
+ wrefresh(win(List));
+ return FALSE;
+#ifdef KEY_RESIZE
+ case KEY_RESIZE:
+ resizeterm(LINES, COLS);
+ checktermsize();
+ { enum win w; for(w = (enum win) 0; w != wCount; w++) {
+ delwin(window[w].win);
+ window[w].win = newwin(wHeight(w), wWidth(w), wTop(w), wLeft(w));
+ } }
+ /* this won't work for the help viewer, but it doesn't use yesno() */
+ currentitem = items;
+ topy = 0;
+ draw();
+ wattrset(win(Input), COLOR_PAIR(4) | A_BOLD | A_REVERSE);
+ mvwhline(win(Input), 0, 0, ' ', wWidth(Input));
+ waddstr(win(Input), prompt);
+ whline(win(Input), 'Y', 1);
+ wrefresh(win(Input));
+ break;
+#endif
+ }
+}
+
+int maineventloop(
+ const char *_subtitle,
+ int(*_callback)(struct item **, int),
+ void(*_drawitem)(struct item *, bool),
+ struct item *_items,
+ const struct key *_keys) {
+ int result;
+
+ { const char *temp = subtitle;
+ subtitle=_subtitle;
+ _subtitle=temp; }
+ { void(*temp)(struct item *, bool) = drawitem;
+ drawitem=_drawitem;
+ _drawitem=temp; }
+ { struct item *temp=items;
+ items=_items;
+ _items=temp; }
+ { const struct key *temp=keys;
+ keys=_keys;
+ _keys=temp; }
+
+ currentitem = items;
+ topy = 0;
+
+ draw();
+
+ for(;;) {
+ int c = getch();
+#ifndef NCURSES_MOUSE_VERSION
+ if(c==ERR)
+ continue;
+#else
+ static int mousekey = ERR;
+ if(c==ERR) {
+ if(errno!=EINTR && mousekey!=ERR)
+ c = mousekey;
+ else
+ continue;
+ }
+
+ check_key:
+ if(c==KEY_MOUSE) {
+ MEVENT event;
+ if(getmouse(&event)==OK) {
+ if(mousekey != ERR && event.bstate & (BUTTON1_CLICKED | BUTTON1_DOUBLE_CLICKED | BUTTON1_RELEASED)) {
+ cbreak();
+ mousekey = ERR;
+ if(!(event.bstate & (BUTTON1_CLICKED | BUTTON1_DOUBLE_CLICKED)))
+ continue;
+ }
+ if(wmouse_trafo(win(List), &event.y, &event.x, FALSE)) {
+ if(event.bstate & (BUTTON1_CLICKED | BUTTON1_DOUBLE_CLICKED)) {
+ struct item *item = currentitem;
+ if(currentitem->top-topy > event.y) {
+ do item = item->prev;
+ while((item==items ? item=NULL, 0 : 1)
+ && item->top-topy > event.y);
+ } else if(currentitem->top-topy+currentitem->height-1 < event.y) {
+ do item = item->next;
+ while((item->next==items ? item=NULL, 0 : 1)
+ && item->top-topy+item->height-1 < event.y);
+ }
+ if(item==NULL)
+ continue;
+ (*drawitem)(currentitem, FALSE);
+ currentitem = item;
+ if(event.bstate & BUTTON1_DOUBLE_CLICKED) {
+ result=(*_callback)(&currentitem, KEY_MOUSE);
+ if(result>=0)
+ goto exit;
+ }
+ scrollcurrent();
+ (*drawitem)(currentitem, TRUE);
+ }
+ } else if(wmouse_trafo(win(Scrollbar), &event.y, &event.x, FALSE)) {
+ if(event.bstate & (BUTTON1_PRESSED | BUTTON1_CLICKED | BUTTON1_DOUBLE_CLICKED)
+ && event.y < wHeight(Scrollbar)-1) {
+ halfdelay(1);
+
+#define SIM(key) \
+ { \
+ c = KEY_ ## key; \
+ if(event.bstate & BUTTON1_PRESSED) \
+ mousekey = c; \
+ goto check_key; \
+ }
+ if(event.y == 0)
+ SIM(UP)
+ else if(event.y == wHeight(Scrollbar)-2)
+ SIM(DOWN)
+ else if(event.y-1 < (wHeight(Scrollbar)-3)*topy/(items->prev->top+items->prev->height-(wHeight(List)-1)))
+ SIM(PPAGE)
+ else if(event.y-1 > (wHeight(Scrollbar)-3)*topy/(items->prev->top+items->prev->height-(wHeight(List)-1)))
+ SIM(NPAGE)
+#undef SIM
+ else
+ if(event.bstate & BUTTON1_PRESSED) {
+ for(;;) {
+ c = getch();
+ switch(c) {
+ default:
+ goto check_key;
+ case ERR:
+ continue;
+ case KEY_MOUSE:
+ if(getmouse(&event)==OK) {
+ event.y -= wTop(Scrollbar)+1;
+ if(event.y>=0 && event.y<wHeight(Scrollbar)-3) {
+ topy = (event.y*(items->prev->top+items->prev->height-(wHeight(List)-1))+(wHeight(Scrollbar)-4))/(wHeight(Scrollbar)-3);
+ while(currentitem!=items
+ && currentitem->prev->top >= topy)
+ currentitem = currentitem->prev;
+ while(currentitem->next!=items
+ && currentitem->top < topy)
+ currentitem = currentitem->next;
+ if(currentitem->top+currentitem->height > topy+wHeight(List))
+ topy = currentitem->top+currentitem->height - wHeight(List);
+ drawitems();
+ drawscrollbar();
+ wrefresh(win(List));
+ }
+ }
+ }
+ break;
+ }
+ }
+ }
+ } else if(wmouse_trafo(win(Bottom), &event.y, &event.x, FALSE)) {
+ if(event.bstate & (BUTTON1_CLICKED | BUTTON1_DOUBLE_CLICKED)
+ && event.y == 1) {
+ const struct key *key;
+ int x = event.x;
+ if(x < 2)
+ continue;
+ x -= 2;
+ for(key = keys; key->key!='\0'; key++) {
+ if(x < key->length) {
+ event.x -= x;
+ wattrset(win(Bottom), COLOR_PAIR(3) | A_BOLD | A_REVERSE);
+ mvwaddstr(win(Bottom), event.y, event.x, key->descr);
+ wmove(win(Bottom), event.y, event.x);
+ wrefresh(win(Bottom));
+ usleep(100000);
+ wattrset(win(Bottom), COLOR_PAIR(3));
+ waddstr(win(Bottom), key->descr);
+ wnoutrefresh(win(Bottom));
+ c = key->key;
+ goto check_key;
+ }
+ x -= key->length;
+ if(x == 0)
+ break;
+ x--;
+ }
+ }
+ }
+ }
+ } else
+#endif
+ {
+ result=(*_callback)(&currentitem, c);
+ if(result>=0)
+ goto exit;
+
+ switch(c) {
+ case KEY_UP:
+ if(currentitem!=items) {
+ (*drawitem)(currentitem, FALSE);
+ currentitem = currentitem->prev;
+ scrollcurrent();
+ (*drawitem)(currentitem, TRUE);
+ }
+ break;
+
+ case KEY_DOWN:
+ if(currentitem->next!=items) {
+ (*drawitem)(currentitem, FALSE);
+ currentitem = currentitem->next;
+ scrollcurrent();
+ (*drawitem)(currentitem, TRUE);
+ }
+ break;
+
+ case KEY_PPAGE:
+ if(currentitem!=items) {
+ struct item *olditem = currentitem;
+ (*drawitem)(currentitem, FALSE);
+ while(currentitem!=items
+ && olditem->top - currentitem->prev->top <= wHeight(List))
+ currentitem = currentitem->prev;
+ scrollcurrent();
+ (*drawitem)(currentitem, TRUE);
+ }
+ break;
+
+ case KEY_NPAGE:
+ if(currentitem->next!=items) {
+ struct item *olditem = currentitem;
+ (*drawitem)(currentitem, FALSE);
+ while(currentitem->next!=items
+ && (currentitem->next->top + currentitem->next->height)
+ - (olditem->top + olditem->height) <= wHeight(List))
+ currentitem = currentitem->next;
+ scrollcurrent();
+ (*drawitem)(currentitem, TRUE);
+ }
+ break;
+
+ case KEY_HOME:
+ if(currentitem!=items) {
+ (*drawitem)(currentitem, FALSE);
+ currentitem = items;
+ scrollcurrent();
+ (*drawitem)(currentitem, TRUE);
+ }
+ break;
+
+ case KEY_END:
+ if(currentitem->next!=items) {
+ (*drawitem)(currentitem, FALSE);
+ currentitem = items->prev;
+ scrollcurrent();
+ (*drawitem)(currentitem, TRUE);
+ }
+ break;
+
+#ifdef KEY_RESIZE
+ case KEY_RESIZE:
+ resizeterm(LINES, COLS);
+ checktermsize();
+ { enum win w; for(w = (enum win) 0; w != wCount; w++) {
+ delwin(window[w].win);
+ window[w].win = newwin(wHeight(w), wWidth(w), wTop(w), wLeft(w));
+ } }
+ if(result==-1)
+ currentitem = items;
+ else
+ items = currentitem;
+ topy = 0;
+ draw();
+ break;
+#endif
+ }
+ }
+ doupdate();
+ }
+exit:
+ { const char *temp = subtitle;
+ subtitle=_subtitle;
+ _subtitle=temp; }
+ { void(*temp)(struct item *, bool) = drawitem;
+ drawitem=_drawitem;
+ _drawitem=temp; }
+ { struct item *temp=items;
+ items=_items;
+ _items=temp; }
+ { const struct key *temp=keys;
+ keys=_keys;
+ _keys=temp; }
+
+ if(items!=NULL) {
+ currentitem = items;
+ topy = 0;
+ draw();
+ }
+
+ return result;
+}
diff --git a/ufed-curses.h b/ufed-curses.h
new file mode 100644
index 0000000..0456384
--- /dev/null
+++ b/ufed-curses.h
@@ -0,0 +1,50 @@
+#define C99 (__STDC_VERSION__ >= 199901L)
+#if !C99
+#if __GNUC__ < 2 || __GNUC__ == 2 && __GNUC_MINOR__ < 95
+#error Either a C99 compiler, or gcc 2.95.* or higher is required.
+#endif
+#endif
+
+#include <curses.h>
+
+#if !C99
+#define inline __inline
+#endif
+
+enum win { Top, Left, List, Input, Scrollbar, Right, Bottom, wCount };
+struct window {
+ WINDOW *win;
+ const int top, left, height, width;
+};
+struct item {
+ struct item *prev, *next;
+ int top, height;
+};
+struct key {
+ char key;
+ const char *descr;
+ int length;
+};
+
+extern struct window window[wCount];
+
+extern void initcurses(void);
+extern void cursesdone(void);
+
+extern int maineventloop(
+ const char *subtitle,
+ int (*callback)(struct item **currentitem, int key),
+ void(*drawitem)(struct item *item, bool highlight),
+ struct item *items,
+ const struct key *keys);
+extern void scrollcurrent(void);
+extern bool yesno(const char *);
+
+static inline WINDOW *win(enum win w) { return window[w].win; }
+static inline int wTop (enum win w) { return (window[w].top >= 0 ? 0 : LINES) + window[w].top ; }
+static inline int wLeft (enum win w) { return (window[w].left >= 0 ? 0 : COLS ) + window[w].left ; }
+static inline int wHeight(enum win w) { return (window[w].height > 0 ? 0 : LINES) + window[w].height; }
+static inline int wWidth (enum win w) { return (window[w].width > 0 ? 0 : COLS ) + window[w].width ; }
+
+extern int minheight, minwidth;
+extern int topy;
diff --git a/ufed.8 b/ufed.8
new file mode 100644
index 0000000..7d7fe50
--- /dev/null
+++ b/ufed.8
@@ -0,0 +1,97 @@
+.TH "UFED" "8" "24 Apr 2002" "UFED 0.34" "UFED"
+.SH "NAME"
+ufed \- Gentoo Linux USE flags editor
+.SH "SYNOPSIS"
+.B ufed
+.SH "DESCRIPTION"
+UFED is a simple program designed to help you configure the systems USE flags
+(see below) to your liking. To select of unselect a flag highlight it and hit
+space.
+
+UFED attempts to show you where a particular use setting came from. Each USE
+flag has a 3 character descriptor that represents the three ways a use flag can
+be set.
+
+The 1st char is the setting from the /etc/make.profile/make.defaults file.
+These are the defaults for Gentoo as a whole. These should not be changed.
+
+The 2nd char is the setting from the /etc/make.profile/use.defaults file. These
+will change as packages are added and removes from the system.
+
+The 3rd char is the settings from the /etc/make.conf file. these are the only
+ones that should be changed by the user and these are the ones that UFED
+changes.
+
+If the character is a + then that USE flag was set in that file, if it is a
+space then the flag was not mentioned in that file and if it is a - then that
+flag was unset in that file.
+
+.B What are USE flags?
+
+The USE settings system is a flexible way to enable or disable various features
+at package build-time on a global level and for individual packages. This
+allows an administrator control over how packages are built in regards to the
+optional features which can be compiled into those packages.
+
+For instance, packages with optional GNOME support can have this support
+disabled at compile time by disabling the "gnome" USE setting. Enabling the
+"gnome" USE setting would enable GNOME support in these same packages.
+
+The effect of USE settings on packages is dependent on whether both the
+software itself and the package ebuild supports the USE setting as an optional
+feature. If the software does not have support for an optional feature then the
+corresponding USE setting will obviously have no effect.
+
+Also many package dependencies are not considered optional by the software and
+thus USE settings will have no effect on those mandatory dependencies.
+
+A list of USE keywords used by a particular package can be found by checking
+the IUSE line in any ebuild file.
+
+See http://www.gentoo.org/doc/en/handbook/handbook-x86.xml?part=2&chap=1
+for more information on USE flags.
+
+Please also note that if UFED describes a flag (Unknown) it generally means
+that it is either a spelling error in one of the 3 configuration files or
+it is not an officially sanctioned USE flag. Sanctioned USE flags can be found
+in /usr/portage/profiles/use.desc and in /usr/portage/profiles/use.local.desc.
+
+.SH "REPORTING BUGS"
+Please report bugs via http://bugs.gentoo.org/
+.SH "SEE ALSO"
+.BR emerge (1),
+.BR ebuild (5),
+.BR make.conf (5)
+.SH "FILES"
+.TP
+\fB/etc/make.conf\fR
+Contains user specified USE flags
+.TP
+\fB/etc/make.conf.old \fR
+This is where ufed places a backup of your make.conf file.
+.TP
+\fB/etc/make.profile/make.defaults\fR
+Contains system default USE flags
+.TP
+\fB/etc/make.profile/use.defaults\fR
+Provides an automatic ebuild to USE flag mapping ('auto' flags)
+.TP
+\fB/etc/make.profile/use.mask\fR
+Restricted USE flags
+.TP
+\fB/usr/portage/profiles/use.desc\fR
+Description strings for global USE flags
+.TP
+\fB/usr/portage/profiles/use.local.desc\fR
+Description strings for local USE flags
+.SH "AUTHORS"
+Robin Johnson <robbat2@gentoo.org>
+.br
+Fred Van Andel <fava@gentoo.org>
+.br
+Arun Bhanu <codebear@gentoo.org>
+.br
+Maik Schreiber <blizzy@gentoo.org> (original author)
+.br
+.SH "CVS HEADER"
+$Header: /var/cvsroot/gentoo-src/ufed/docs/ufed.8,v 1.4 2004/06/03 05:00:04 fava Exp $"
diff --git a/ufed.pl b/ufed.pl
new file mode 100644
index 0000000..b034c84
--- /dev/null
+++ b/ufed.pl
@@ -0,0 +1,286 @@
+#!/usr/bin/perl -I/usr/lib/ufed
+use strict;
+use warnings;
+
+# Copyright 1999-2005 Gentoo Foundation
+# Distributed under the terms of the GNU General Public License v2
+# $Header: /var/cvsroot/gentoo-src/ufed/ufed.pl,v 1.45 2005/10/18 11:14:33 truedfx Exp $
+
+use Portage;
+
+my $version = '0.40';
+
+my %use_descriptions;
+
+sub finalise(@);
+sub flags_dialog();
+sub read_use_descs();
+sub save_flags(@);
+
+delete $Portage::all_flags{'*'};
+
+read_use_descs;
+
+delete @use_descriptions{qw(bootstrap build)};
+
+$Portage::all_flags{'-*'} = 1 if defined $Portage::make_conf_flags{'*'} && !$Portage::make_conf_flags{'*'};
+
+Portage::merge %Portage::use_masked_flags, %Portage::archs;
+
+for(keys %Portage::all_flags) {
+ @{$use_descriptions{$_}} = "(Unknown)"
+ if not exists $use_descriptions{$_};
+}
+@{$use_descriptions{'-*'}} = 'Never enable any flags other than those specified in /etc/make.conf';
+
+for(keys %Portage::use_masked_flags)
+{ delete $use_descriptions{$_} if $Portage::use_masked_flags{$_} }
+
+flags_dialog;
+
+sub finalise(@) {
+ my %flags;
+ @flags{@_} = ();
+ if(exists $flags{'-*'}) {
+ return sort keys %flags;
+ } else {
+ my(@enabled, @disabled);
+ my %all_flags;
+ @all_flags{keys %flags, keys %Portage::default_flags} = ();
+ for(sort keys %all_flags) {
+ next if $_ eq '*';
+ push @enabled, $_ if exists $flags{$_} && !$Portage::default_flags{$_};
+ push @disabled, "-$_" if $Portage::default_flags{$_} && !exists $flags{$_};
+ }
+ return @enabled, @disabled;
+ }
+}
+
+sub flags_dialog() {
+ use POSIX ();
+ POSIX::dup2 1, 3;
+ POSIX::dup2 1, 4;
+ my ($iread, $iwrite) = POSIX::pipe;
+ my ($oread, $owrite) = POSIX::pipe;
+ my $child = fork;
+ die "fork() failed\n" if not defined $child;
+ if($child == 0) {
+ POSIX::close $iwrite;
+ POSIX::close $oread;
+ POSIX::dup2 $iread, 3;
+ POSIX::close $iread;
+ POSIX::dup2 $owrite, 4;
+ POSIX::close $owrite;
+ my $interface = 'ufed-curses';
+ exec { "/usr/lib/ufed/$interface" } $interface or
+ do { print STDERR "Couldn't launch $interface\n"; exit 3 }
+ }
+ POSIX::close $iread;
+ POSIX::close $owrite;
+ if(open my $fh, '>&=', $iwrite) {
+ my @flags = sort { uc $a cmp uc $b } keys %use_descriptions;
+ my %descriptions;
+ for(my $flag=0; $flag<@flags; $flag++) {
+ my $flag = $flags[$flag];
+ print $fh $flag, $Portage::all_flags{$flag} ? ' on ' : ' off ';
+ print $fh exists $Portage::make_defaults_flags{$flag} ? $Portage::make_defaults_flags{$flag} ? '(+' :'(-' :'( ' ;
+ print $fh exists $Portage::use_defaults_flags{$flag} ? $Portage::use_defaults_flags{$flag} ? '+' : '-' : ' ' ;
+ print $fh exists $Portage::make_conf_flags{$flag} ? $Portage::make_conf_flags{$flag} ? '+)': '-)': ' )';
+ print $fh ' ', scalar(@{$use_descriptions{$flag}}), "\n";
+ print $fh $_, "\n" for(@{$use_descriptions{$flag}});
+ }
+ close $fh;
+ } else {
+ die "Couldn't let interface know of flags\n";
+ }
+ POSIX::close $iwrite;
+ wait;
+ open my $fh, '<&=', $oread or die "Couldn't read output.\n";
+ if(POSIX::WIFEXITED($?)) {
+ my $rc = POSIX::WEXITSTATUS($?);
+ if($rc==0) {
+ my @flags = do { local $/; split /\n/, <$fh> };
+ save_flags finalise sort @flags;
+ } elsif($rc==1)
+ { print "Cancelled, not saving changes.\n" }
+ exit $rc;
+ } elsif(POSIX::WIFSIGNALED($?))
+ { kill POSIX::WTERMSIG($?), $$ }
+ else
+ { exit 127 }
+}
+
+sub read_use_descs() {
+ my %_use_descriptions;
+ for my $dir(@Portage::portagedirs) {
+ for(Portage::noncomments "$dir/profiles/use.desc") {
+ my ($flag, $desc) = /^(.*?)\s+-\s+(.*)$/ or next;
+ $_use_descriptions{$flag}{$desc} = 1;
+ }
+ }
+ my %_use_local_descriptions;
+ for my $dir(@Portage::portagedirs) {
+ for(Portage::noncomments "$dir/profiles/use.local.desc") {
+ my ($pkg, $flag, $desc) = /^(.*?):(.*?)\s+-\s+(.*)$/ or next;
+ $_use_local_descriptions{$flag}{$desc}{$pkg} = 1;
+ }
+ }
+ local $"=", ";
+ for(sort keys %_use_descriptions)
+ { @{$use_descriptions{$_}} = sort keys %{$_use_descriptions{$_}} }
+ for(sort keys %_use_local_descriptions) {
+ for my $desc(sort keys %{$_use_local_descriptions{$_}})
+ { push @{$use_descriptions{$_}}, "Local flag: $desc (@{[sort keys %{$_use_local_descriptions{$_}{$desc}}]})" }
+ }
+}
+
+sub save_flags(@) {
+ my $BLANK = qr{(?:[ \n\t]+|#.*)+}; # whitespace and comments
+ my $UBLNK = qr{(?: # as above, but scan for #USE=
+ [ \n\t]+ |
+ \#[ \t]*USE[ \t]*=.*(\n?) | # place capture after USE=... line
+ \#.*)+}x;
+ my $IDENT = qr{([^ \\\n\t'"{}=]+)}; # identifiers
+ my $ASSIG = qr{=}; # assignment operator
+ my $UQVAL = qr{(?:[^ \\\n\t'"]+|\\.)+}s; # unquoted value
+ my $SQVAL = qr{'[^']*'}; # singlequoted value
+ my $DQVAL = qr{"(?:[^\\"]|\\.)*"}s; # doublequoted value
+ my $BNUQV = qr{(?:[^ \\\n\t'"]+|\\\n()|\\.)+}s; # unquoted value (scan for \\\n)
+ my $BNDQV = qr{"(?:[^\\"]|\\\n()|\\.)*"}s; # doublequoted value (scan for \\\n)
+
+ my (@flags) = @_;
+ my $contents;
+
+ {
+ open my $makeconf, '<', '/etc/make.conf' or die "Couldn't open /etc/make.conf\n";
+ open my $makeconfold, '>', '/etc/make.conf.old' or die "Couldn't open /etc/make.conf.old\n";
+ local $/;
+ $_ = <$makeconf>;
+ print $makeconfold $_;
+ close $makeconfold;
+ close $makeconf;
+ }
+
+ eval {
+ # USE comment start/end (start/end of newline character at the end, specifically)
+ # default to end of make.conf, to handle make.confs without #USE=
+ my($ucs, $uce) = (length, length);
+ my $flags = '';
+ pos = 0;
+ for(;;) {
+ if(/\G$UBLNK/gc) {
+ ($ucs, $uce) = ($-[1], $+[1]) if defined $1;
+ }
+ last if pos == length;
+ my $flagatstartofline = do {
+ my $linestart = 1+rindex $_, "\n", pos()-1;
+ my $line = substr($_, $linestart, pos()-$linestart);
+ $line !~ /[^ \t]/;
+ };
+ /\G$IDENT/gc or die;
+ my $name = $1;
+ /\G$BLANK/gc;
+ /\G$ASSIG/gc or die;
+ /\G$BLANK/gc;
+ die if pos == length;
+ if($name ne 'USE') {
+ /\G(?:$UQVAL|$SQVAL|$DQVAL)+/gc or die;
+ } else {
+ my $start = pos;
+ /\G(?:$BNUQV|$SQVAL|$BNDQV)+/gc or die;
+ my $end = pos;
+ # save whether user uses backslash-newline
+ my $bsnl = defined $1 || defined $2;
+ # start of the line is one past the last newline; also handles first line
+ my $linestart = 1+rindex $_, "\n", $start-1;
+ # everything on the current line before the USE flags, plus one for the "
+ my $line = substr($_, $linestart, $start-$linestart).' ';
+ # only indent if USE starts a line
+ my $blank = $flagatstartofline ? $line : "";
+ $blank =~ s/[^ \t]/ /g;
+ # word wrap
+ if(@flags != 0) {
+ my $length = 0;
+ while($line =~ /(.)/g) {
+ if($1 ne "\t") {
+ $length++;
+ } else {
+ # no best tab size discussions, please. terminals use ts=8.
+ $length&=~8;
+ $length+=8;
+ }
+ }
+ my $blanklength = $blank ne '' ? $length : 0;
+ # new line, using backslash-newline if the user did that
+ my $nl = ($bsnl ? " \\\n" : "\n").$blank;
+ my $linelength = $bsnl ? 76 : 78;
+ my $flag = $flags[0];
+ if($blanklength != 0 || length $flag <= $linelength) {
+ $flags = $flag;
+ $length += length $flag;
+ } else {
+ $flags = $nl.$flag;
+ $length = length $flag;
+ }
+ for $flag(@flags[1..$#flags]) {
+ if($length + 1 + length $flag <= $linelength) {
+ $flags .= " $flag";
+ $length += 1+length $flag;
+ } else {
+ $flags .= $nl.$flag;
+ $length = $blanklength + length $flag;
+ }
+ }
+ }
+ # replace the current USE flags with the modified ones
+ substr($_, $start, $end-$start) = "\"$flags\"";
+ # and have the next search start after our new flags
+ pos = $start + 2 + length $flags;
+ # and end this
+ undef $flags;
+ last;
+ }
+ }
+ if(defined $flags) { # if we didn't replace the flags, tack them after the last #USE= or at the end
+ $flags = '';
+ if(@flags != 0) {
+ $flags = $flags[0];
+ my $length = 5 + length $flags[0];
+ for my $flag(@flags[1..$#flags]) {
+ if($length + 1 + length $flag <= 78) {
+ $flags .= " $flag";
+ $length += 1+length $flag;
+ } else {
+ $flags .= "\n $flag";
+ $length = 5+length $flag;
+ }
+ }
+ }
+ substr($_, $ucs, $uce-$ucs) = "\nUSE=\"$flags\"\n";
+ } else { # if we replaced the flags, delete any further overrides
+ for(;;) {
+ my $start = pos;
+ /\G$BLANK/gc;
+ last if pos == length;
+ /\G$IDENT/gc or die;
+ my $name = $1;
+ /\G$BLANK/gc;
+ /\G$ASSIG/gc or die;
+ /\G$BLANK/gc;
+ /\G(?:$UQVAL|$SQVAL|$DQVAL)+/gc or die;
+ my $end = pos;
+ if($name eq 'USE') {
+ substr($_, $start, $end-$start) = '';
+ pos = $start;
+ }
+ }
+ }
+ };
+ die "Parse error when writing make.conf - did you modify it while ufed was running?\n" if $@;
+
+ {
+ open my $makeconf, '>', '/etc/make.conf' or die "Couldn't open /etc/make.conf\n";
+ print $makeconf $_;
+ close $makeconf;
+ }
+}