diff options
-rw-r--r-- | COPYING | 340 | ||||
-rw-r--r-- | Makefile | 17 | ||||
-rw-r--r-- | Portage.pm | 282 | ||||
-rwxr-xr-x | configure | 20 | ||||
-rw-r--r-- | ufed-curses-checklist.c | 358 | ||||
-rw-r--r-- | ufed-curses-help.c | 220 | ||||
-rw-r--r-- | ufed-curses-help.h | 1 | ||||
-rw-r--r-- | ufed-curses.c | 564 | ||||
-rw-r--r-- | ufed-curses.h | 50 | ||||
-rw-r--r-- | ufed.8 | 97 | ||||
-rw-r--r-- | ufed.pl | 286 |
11 files changed, 2235 insertions, 0 deletions
@@ -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)(¤titem, 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)(¤titem, 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; @@ -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 $" @@ -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; + } +} |