prt-auf: added the printf subroutine

This commit is contained in:
John McQuah 2022-06-11 14:26:19 -04:00
parent ed198ade6b
commit e1c023b685
2 changed files with 199 additions and 87 deletions

View File

@ -235,9 +235,30 @@ and --tree to get a nicely indented one (note that --tree implies
--recursive).
.TP
.B dup
.B dup [format string]
List ports which can be found in multiple directories configured in
.B /etc/prt-get.conf
The optional format string can contain any of the following variables,
allowing you to see at a glance how the ports differ.
.TP
\ \ \ \(bu
%n \-> name
.TP
\ \ \ \(bu
%p1, %p2 \-> path to the {higher, lower}-priority port
.TP
\ \ \ \(bu
%v1, %v2 \-> version of the {higher, lower}-priority port
.TP
\ \ \ \(bu
%u1, %u2 \-> upstream URL of the {higher, lower}-priority port
.TP
\ \ \ \(bu
%M1, %M2 \-> maintainer of the {higher, lower}-priority port
.TP
.B list [\-v|\-vv]
@ -245,6 +266,75 @@ List ports available in the ports tree. It's basically the same as
.B ports \-l
but looks in all directories specified in the config file.
.TP
.B printf <format string> [\-\-regex] [\-\-filter=<filter>]
Print formatted port list. <format string> can contain variables, which
are replaced like this:
.TP
\ \ \ \(bu
%n \-> name
.TP
\ \ \ \(bu
%p \-> path
.TP
\ \ \ \(bu
%v \-> version
.TP
\ \ \ \(bu
%r \-> release
.TP
\ \ \ \(bu
%d \-> description
.TP
\ \ \ \(bu
%e \-> dependencies
.TP
\ \ \ \(bu
%u \-> URL
.TP
\ \ \ \(bu
%P -> Packager
.TP
\ \ \ \(bu
%M -> Maintainer
.TP
\ \ \ \(bu
%R -> Readme ("yes"/"no")
.TP
\ \ \ \(bu
%E -> pre-install script ("yes"/"no")
.TP
\ \ \ \(bu
%O -> post-install script ("yes"/"no")
.TP
\ \ \ \(bu
%l -> is locked ("yes"/"no")
.TP
\ \ \ \(bu
%i \-> "no" if not installed, "yes" if it's installed and
up to date and "diff" if it's installed and a new version is in the
ports tree.
Use "\\n" and "\\t" to format your output (note that "\\n" is appended to your
format specifier automatically). To restrict the list to package names
matching a desired pattern, you can specify
.B filter
Metacharacters in the filter are only respected if you pass the --regex option,
in which case your filter should be a Perl-compatible regular expression.
.TP
.B listinst [\-v|\-vv]
List installed ports. It's basically the same as
@ -451,18 +541,6 @@ or
.B vim $(prt\-auf path $desired_port)/Pkgfile
are perfectly cromulent ways to do the same thing.
.TP
\ \ \ \(bu no 'printf' action (yet). This subcommand is needed by some of the \fBprt\-utils\fP
scripts, so it might be one of the first features to come back in the next version of
\fBprt\-auf\fP. The subroutine \fIdump_flat_db()\fP already loads most of the data structures
needed to generate formatted output; the missing component is a new code block in the argument
parser that will translate user requests into the variables used internally by \fBprt\-auf\fP.
.TP
\ \ \ \(bu no "filter" arguments recognized by 'diff', 'list', etc. There is no plan to add such a
feature in a later version of \fBprt\-auf\fP, because the newline-separated output of 'diff' and
'list' is easily piped to awk or grep for any desired filtering.
.TP
\ \ \ \(bu no version comparator. One of the main reasons to run CRUX is to stay current with the
latest stable versions endorsed by the port maintainers. (They subscribe to the upstream mailing
@ -470,14 +548,25 @@ lists so you don't have to.) If you want to keep a particular piece of software
version than the one chosen by its original maintainer, you can maintain a shadow port in your own
overlay (and put that overlay higher in the config file).
.TP
\ \ \ \(bu no wildcards or shell globbing in the search commands. Being written in Perl,
\fBprt\-auf\fP automatically inherits a rich set of routines for dealing with regular expressions.
When paired with the case-insensitive pattern matching of purely-alphanumeric queries, the Perl regexp
engine offers CRUX users enough flexibility to find any port they're looking for, without needing to
clutter the code base by reimplementing shell wildcards.
.TP
\ \ \ \(bu no "--ignore" switch. This feature is easy enough to add at a later date, but a newcomer
to CRUX will likely be confused at having such fine-grained control over the automatic dependency
resolution. Getting into the habit of using "--ignore" for any port with a questionable
"Depends on" line will discourage the CRUX newcomer from providing valuable feedback to the port
maintainers. Reporting the erroneous "Depends on" line will make the port collection better for
everyone, while a one-time use of "--ignore" will keep those improvements from reaching a wider
audience.
resolution. The two main reasons to use "--ignore" (an erroneous "Depends on" line, or a
satisfaction of the dependency by manual installations that pkgutils is not aware of), are both more
properly addressed by a long-term solution rather than a one-time fix. If the "Depends on" line is
truly in error, the CRUX user should contact the port maintainer and get it fixed for everybody,
rather than passing the "--ignore" option and letting the error go uncorrected. If the dependency
was satisfied by a manual installation outside of pkgutils, a better response is to make a dummy
port and create an entry in the aliases file. That way prt-auf will treat the dependency as
satisfied for any subsequent installations on the same machine, and passing the "--ignore" option
will be unnecessary for all future ports with the same dependency.
.SH "EXAMPLES"
.TP

View File

@ -32,7 +32,7 @@ my ($action, @query) = parse_args(@ARGV);
# load some data structures into memory for the actions that need them
if ($action !~ /^(fsearch|isinst|current)$/) {
@allports = list_ports();
fill_hashes_from_pkgfiles();
fill_hashes_from_pkgfiles() unless ($action eq "printf");
}
if ($osearch{cache}==1) { fill_hashes_from_cache(); }
@ -49,37 +49,30 @@ if ($action =~ /^(diff|quickdiff|listlocked|depends|deptree|remove|install|updat
############## Branch based on the requested action #################
if ($action eq "path") {
@results = find_port_by_name($query[0],1,1,0);
} elsif ($action eq "search") {
@results = find_port_by_name($query[0],0,$osearch{path},1);
} elsif ($action eq "fsearch") {
$hh = find_port_by_file(".footprint", $query[-1]);
} elsif ($action eq "dsearch") {
@results = find_port_by_desc($query[0]);
} elsif ($action eq "info") {
$portpath = find_port_by_name($query[0],1,1,0);
if ($action eq "path") { @results = find_port_by_name(@query,1,1,0);
} elsif ($action eq "search") { @results = find_port_by_name(@query,0,$osearch{path},1);
} elsif ($action eq "fsearch") { $hh = find_port_by_file(".footprint", $query[-1]);
} elsif ($action eq "dsearch") { @results = find_port_by_desc(@query);
} elsif ($action eq "info") { $portpath = find_port_by_name(@query,1,1,0);
@results = get_pkgfile_fields($portpath,"all") if ($portpath);
} elsif ($action eq "cache") { dump_flat_db();
} elsif ($action eq "cache") { printf_ports("CACHE",@allports);
} elsif ($action eq "printf") { @results = ($osearch{filter}) ?
find_port_by_name($osearch{filter},0,1,1) : @allports;
printf_ports($query[0],@results);
} elsif ($action eq "lock") { port_lock(@query);
} elsif ($action eq "unlock") { port_unlock(@query);
} elsif ($action eq "dup") { find_dups();
} elsif ($action eq "ls") { port_ls($query[0]);
} elsif ($action =~ /^(cat|edit|readme)$/) { port_edit($1,@query);
} elsif ($action =~ /^(depends|deptree|quickdep)$/) {
@results=deporder($1,@query);
} elsif ($action eq "dependent") {
@results=list_ports("dependent",@query);
} elsif ($action eq "remove") { $ind=uninstall(@query);
} elsif ($action =~ /^(depends|deptree|quickdep)$/) { @results=deporder($1,@query);
} elsif ($action eq "dependent") { @results=list_ports("dependent",@query);
} elsif ($action eq "sysup") { @results = sysup();
} elsif ($action =~ /^(install|update|depinst|grpinst)$/) {
@results = up_inst($1,@query);
} elsif ($action =~ /^(isinst|current)$/) {
$ind = port_diff($1,@query);
} elsif ($action =~ /(.*)diff$/) {
$ind = port_diff($1);
} elsif ($action =~ /^list(.*)/) {
@results = list_ports($1);
} elsif ($action eq "dup") { $ind=find_dups(@query);
} elsif ($action eq "remove") { $ind=uninstall(@query);
} elsif ($action =~ /^(isinst|current)$/) { $ind = port_diff($1,@query);
} elsif ($action =~ /(.*)diff$/) { $ind = port_diff($1);
} elsif ($action =~ /^list(.*)/) { @results = list_ports($1);
} elsif ($action eq "help") { print_help();
} elsif ($action eq "version") { print "$title $version\n";
} else { printf "Unsupported command '$action'.\n"; }
@ -109,7 +102,7 @@ if (($action =~ /^(listinst|listorphans)/)
chomp($hits{$fh});
printf $strf, $fh, (split /\s/, $hits{$fh})[-1];
}
} elsif ($action =~ /^(diff|quickdiff|current|isinst)$/) {
} elsif ($action =~ /^(diff|quickdiff|current|isinst|dup)$/) {
exit $ind;
} elsif ($action =~ /^(depends|deptree|quickdep)$/) {
print "-- dependencies ([i] = installed, [a] = provided by an alias)\n" if ($action =~ /^dep/);
@ -136,15 +129,13 @@ if (($action =~ /^(listinst|listorphans)/)
}
print "\n" if ($action eq "quickdep");
} elsif ($action eq "info") {
$strf = "%14s: %s\n";
$strf = "%14s: %-s\n";
exit 1 if ($#results < 0);
my @fields = ("Name", "Repository", "Version", "Release", "Description",
"Dependencies", "URL", "Packager", "Maintainer",
"Readme", "PreInstall", "PostInstall");
$results[1] =~ s/^(.*)\/.*$/$1/;
for (my $i=0; $i<7; $i++) {
printf $strf, $fields[$i], $results[$i];
}
for (my $i=0; $i<7; $i++) { printf $strf, $fields[$i], $results[$i]; }
printf $strf, $fields[8], $results[8];
} elsif ($action eq "remove") {
my %removed = %$ind;
@ -187,17 +178,18 @@ sub parse_args {
if ($arg =~ /^(search|dsearch|fsearch|path|info|list|remove)$/) { $action = $1;
} elsif ($arg =~ /^(install|update|depinst|grpinst|sysup)$/) { $action = $1;
} elsif ($arg =~ /^(lock|unlock|listlocked|current|isinst)$/) { $action = $1;
} elsif ($arg =~ /^(diff|quickdiff|listinst|listorphans)$/) { $action = $1;
} elsif ($arg =~ /^(diff|quickdiff|printf|listinst|listorphans)$/) { $action = $1;
} elsif ($arg =~ /^(depends|deptree|quickdep|dependent|dup)$/) { $action = $1;
} elsif ($arg =~ /^(readme|cat|edit|ls|help|version|cache)$/) { $action = $1;
} elsif ($arg eq "--tree") { $odepends{tree} = 1; $odepends{recursive} = 1;
} elsif ($arg eq "--all") { $odepends{all} = 1;
} elsif ($arg eq "--recursive") { $odepends{recursive} = 1;
} elsif ($arg eq "--cache") { $osearch{cache} = 1;
} elsif ($arg eq "--path") { $osearch{path} = 1;
} elsif ($arg eq "--regex") { $osearch{regex} = 1;
} elsif ($arg =~ /^--filter=(.*)/) { $osearch{filter} = $1;
} elsif ($arg eq "-v") { $osearch{verbose} += 1;
} elsif ($arg eq "-vv") { $osearch{verbose} += 2;
} elsif ($arg eq "--cache") { $osearch{cache} = 1;
} elsif ($arg eq "--test") { $opkg{test} = "yes";
} elsif ($arg eq "-fr") { $opkg{margs} .= " -f";
} elsif ($arg =~ /^(-uf|-if|-us|-is|-ns|-kw)$/) { $opkg{margs} .= " $1";
@ -210,7 +202,7 @@ sub parse_args {
}
if (! $action) { print_help(); }
if (($#query > -1) and
($action =~ /^(diff|quickdiff|cache|list|dup|sysup)/)) {
($action =~ /^(diff|quickdiff|cache|list|sysup)/)) {
print "warning: $1 takes no arguments; ignoring those given.\n";
}
if (($#query > 0) and
@ -263,21 +255,31 @@ sub parse_prt_conf {
}
sub find_dups {
my %seen;
foreach my $pp (@allports) {
my $pn = (split /\//, $pp)[-1];
$seen{$pn}++;
}
my %seen; my $format=shift; my @dupinfo; my @info1; my $dupstr; my @hits;
foreach my $pp (@allports) { my $pn = (split /\//, $pp)[-1]; $seen{$pn}++; }
my @dups = grep { $seen{$_} > 1 } keys %seen;
foreach my $dup (@dups) {
print "$dup\n" if ($osearch{verbose}==0);
my @hits = grep /\/$dup$/, @allports;
print "$hits[0] > $hits[1]\n" if ($osearch{verbose}==1);
if ($osearch{verbose}>1) {
print "* $dup\n";
while (my $h=shift(@hits)) { print " $h\n"; }
}
my %subscripts = ( "%n"=>0, "%p1"=>1, "%v1"=>2, "%u1"=>3, "%M1"=>4,
"%p2"=>5, "%v2"=>6, "%u2"=>7, "%M2"=>8 );
if (($osearch{verbose}==0) and (! $format)) {
foreach my $dup (@dups) { print "$dup\n"; }
} elsif (($osearch{verbose}>0) and (! $format)) {
foreach my $dup (@dups) {
@hits = grep /\/$dup$/, @allports;
print "$hits[0] > $hits[1]\n" if $osearch{verbose}==1;
printf "* %s\n"x(1+$#hits), @hits if $osearch{verbose}>1;
}
} else { # the user has given us a format string; let's respect it
foreach my $dup (@dups) {
@hits = grep /\/$dup$/, @allports;
@dupinfo = (get_pkgfile_fields($hits[0],"all"))[1,2,6,8];
@info1 = (get_pkgfile_fields($hits[1],"all"))[1,2,6,8];
push(@dupinfo,@info1); unshift(@dupinfo,$dup);
$dupstr = "$format\n";
$dupstr =~ s/(%n|%p1|%v1|%u1|%M1|%p2|%v2|%u2|%M2)/$dupinfo[$subscripts{$1}]/g;
print $dupstr;
}
}
return 1+$#dups;
}
sub get_locked_and_aliased {
@ -302,18 +304,36 @@ sub who_aliased_to {
return $who;
}
sub dump_flat_db {
my $FS; my @pstats; my $p;
open (CACHE,'>',$prtcache) or die "cannot create a new cache file";
print CACHE "V5\n";
foreach my $pp (@allports) {
$p = (split /\//, $pp)[-1];
@pstats = get_pkgfile_fields($pp,"all");
for (my $ps=0; $ps<=$#pstats; $ps++) {
printf CACHE "%s\n", $pstats[$ps];
sub printf_ports {
my $FS; my @pstats; my $p; my $inputf=shift; my @targets=@_;
my @pos; my $strf; my $outputf;
my %subscripts = ( "%n"=>0, "%p"=>1, "%v"=>2, "%r"=>3, "%d"=>4, "%e"=>5,
"%u"=>6, "%P"=>7, "%M"=>8, "%R"=>9, "%E"=>10, "%O"=>11, "%l"=>12, "%i"=>13 );
if ($inputf eq "CACHE") {
open (CACHE,'>',$prtcache) or die "cannot create a new cache file";
print CACHE "V5\n";
foreach my $pp (@targets) {
$p = (split /\//, $pp)[-1];
@pstats = (get_pkgfile_fields($pp,"all"));
printf CACHE "%s\n"x($#pstats+1), @pstats;
} close (CACHE);
print "cache created.\n";
} else {
$strf = $inputf;
$strf =~ s/(%p|%n|%v|%r|%d|%e|%u|%P|%M|%R|%E|%O|%l|%i)/_Z_$subscripts{$1}/g;
@pos = grep { s/([0-9]+)(.*)/$1/ } (split /_Z_/, $strf);
$outputf = "$inputf\n";
$outputf =~ s/(%p|%n|%v|%r|%d|%e|%u|%P|%M|%R|%E|%O|%l|%i)/%-14s/g;
foreach my $pp (@targets) {
$p = (split /\//, $pp)[-1];
@pstats = get_pkgfile_fields($pp,"all");
$pstats[11] = grep /^$p$/, keys %V_INST;
$pstats[12] = grep /^$p$/, @LOCKED;
printf STDOUT $outputf, @pstats[@pos];
}
} close (CACHE);
print "cache created.\n";
}
}
sub fill_hashes_from_cache {
@ -461,10 +481,10 @@ sub port_unlock {
}
sub list_ports {
my @found; my $filter = shift;
my @found; my $subset = shift;
our $indent="0 "; our $height=0; our @descendants=(); our @outfile;
if (! $filter) { # empty arg: list all valid ports
if (! $subset) { # empty arg: list all valid ports
foreach my $collection (@basedirs) {
opendir (DIR, $collection) or next;
foreach my $port (sort(readdir DIR)) {
@ -475,32 +495,32 @@ sub list_ports {
foreach my $lp (@localports) {
push (@found, $lp) if (-f "$lp/Pkgfile");
}
} elsif ($filter eq "inst") { @found = keys %V_INST;
} elsif ($filter eq "locked") { @found=@LOCKED;
} elsif ($filter =~ /^(orphans|dependent)$/) {
} elsif ($subset eq "inst") { @found = keys %V_INST;
} elsif ($subset eq "locked") { @found=@LOCKED;
} elsif ($subset =~ /^(orphans|dependent)$/) {
my $seed=shift;
if ( ((! $seed) and ($filter eq "dependent")) or
((shift @_) and ($filter eq "dependent")) ) {
if ( ((! $seed) and ($subset eq "dependent")) or
((shift @_) and ($subset eq "dependent")) ) {
print "dependent requires exactly one argument.\n";
return;
}
if (($filter eq "dependent") and (! find_port_by_name($seed,1,1,0))) {
if (($subset eq "dependent") and (! find_port_by_name($seed,1,1,0))) {
print "$seed not found in the ports tree.\n"; return;
}
our @searchspace=(($filter eq "orphans") or ($odepends{all}==0)) ?
our @searchspace=(($subset eq "orphans") or ($odepends{all}==0)) ?
keys %V_INST : keys %DEPENDS;
@searchspace = grep { defined $DEPENDS{$_} } @searchspace;
if ($filter eq "orphans") {
if ($subset eq "orphans") {
my $inst_deps="";
foreach my $port (@searchspace) {
$inst_deps .= " $DEPENDS{$port} " if ($DEPENDS{$port});
}
@found = grep { $inst_deps !~ / $_ / } @searchspace;
} elsif (($filter eq "dependent") and ($odepends{recursive}==0)) {
} elsif (($subset eq "dependent") and ($odepends{recursive}==0)) {
@found = grep { " $DEPENDS{$_} " =~ / $seed / } @searchspace;
} elsif (($filter eq "dependent") and ($odepends{recursive}==1)) {
} elsif (($subset eq "dependent") and ($odepends{recursive}==1)) {
push @outfile, "$seed";
my @children = grep { " $DEPENDS{$_} " =~ / $seed / } @searchspace;
@ -533,7 +553,9 @@ sub list_ports {
else { }
} # possibilities for the filter have been exhausted
else { }
return @found;
return @found if ((! $subset) or ($subset =~ /^(orphans|dependent|locked)$/));
if (! $osearch{filter}) { return @found; }
else { return grep {$_ !~ /$osearch{filter}/} @found; }
}
sub port_diff { # returns a scalar indicating how many differences were found
@ -543,6 +565,7 @@ sub port_diff { # returns a scalar indicating how many differences were found
if ($dtype !~ /^(current|isinst|utd)/) {
printf "$format", "Port", "Installed", "In Repository" if (! $dtype);
foreach my $p (keys %V_INST) {
next if (($osearch{filter}) and ($p !~ /$osearch{filter}/));
next if ((grep /$p/, @LOCKED) and ($odepends{all}==0));
$lastcol = ($V_REPO{$p}) ? $V_REPO{$p} : "MISSING!";