prt-auf: initial commit

This commit is contained in:
John McQuah 2022-06-10 11:17:22 -04:00
parent b03d116ca6
commit 985bb342e7
2 changed files with 1331 additions and 0 deletions

518
man8/prt-auf.8 Normal file
View File

@ -0,0 +1,518 @@
.\" man page for prt-auf
.\" last edited June 2022 by John McQuah, jmcquah at disroot dot org
.\"
.\" based on original work by Johannes Winkelmann, jw at tks6 dot net
.\"
.\" .PU
.TH "prt-auf" "8" "" "" ""
.SH "NAME"
.LP
prt\-auf \- add/upgrade frontend to the CRUX pkgutils designed to imitate \fBprt\-get\fP(8)
(see http://www.crux.nu for an overview of CRUX ports and pkgutils)
.SH "SYNOPSIS"
.B prt\-auf subcommand [options] <arguments>
.br
.SH "DESCRIPTION"
prt\-auf is a frontend to the CRUX pkgutils, orchestrating their operation
behind the scenes and letting the user focus on higher-level objectives. It
scans both the local ports tree and the installed package database, to
resolve dependency relationships and to determine which installed packages
are out of date. \fBprt\-auf\fP is especially relevant when you want to:
.PP
.TP
\ \ \ \(bu
add/update a package without first determining where in the ports tree
its build instructions and dependencies are located
.TP
\ \ \ \(bu
pass multiple packages on one command line for an add/update operation
.TP
\ \ \ \(bu
show all the dependencies that would be needed by a set of packages
.TP
\ \ \ \(bu
search for ports by name, by description, or by the files they provide
.TP
\ \ \ \(bu
show the upstream url or the maintainer contact information
.PP
\fBprt\-auf\fP basically serves as an intermediary between your high-level
objectives and the specific calls to pkgmk, pkgadd, and pkgrm that would
achieve them. prt\-auf will search for the necessary information itself in
all the port collections specified in its config file. This allows you to
just request a package for installation, without caring where it actually is
located on your file system. prt\-auf was inspired by \fBprt\-get\fP(8) and
offers an essentially identical user experience.
.PP
prt\-auf lets you search for ports by name, find information about ports
(without installing them of course), or print the dependencies of a port (as
a space- or newline-separated list, or with indentation to represent the
tree structure). Note that prt\-auf trusts the port maintainer to provide an
accurate list of dependencies; if this list is incomplete for any of the
ports in your collections, the build might fail.
.PP
prt\-auf has a test mode so you can see what effect an install/update
operation would have. Use the \-\-test switch for this (more details in
the \fBOPTIONS\fP section below).
.SH "RETURN VALUE"
Calling prt\-auf within a shell script sometimes requires you to check its
exit status. Like most Unix tools, prt\-auf returns 0 on success and a
non-zero value otherwise. A typical usage is:
.B if prt\-auf isinst $SOME_PORT; then $TAKE_THIS_ACTION; fi
.SH "SUBCOMMANDS"
prt\-auf uses so\-called subcommands, which always have to be the first
non-option argument passed. This is very similar to
.B git(1).
[subcommand] can be one of the following:
.TP
.B install [\-\-margs=] [\-\-aargs=] <package1> [<package2> ...]
install/update all packages and their dependencies. Any currently-installed
dependency is left at its current version unless explicitly given on the command
line, in which case prt-auf will bring it up to date. If there have been major
version changes in shared libraries since your last update, it might be advisable to
run 'prt-auf update' instead.
.TP
.B update [\-\-margs=] [\-\-aargs=] <package1> [<package2> ...]
bring all the listed packages and their dependencies up to date. Among 'install', 'update',
and 'grpinst', this action is the most permissive, exempting from updates only the locked
ports in the dependency chain. You should use 'install' instead if you only want to build
the missing dependencies.
.TP
.B grpinst [\-\-margs=] [\-\-aargs=] <package1> [<package2> ...]
install/update all packages in the listed order, but stop if pkgmk or pkgadd was
unsuccessful. 'prt-auf grpinst' can be used to override the automatic dependency resolution.
Among 'install', 'update', and 'grpinst', this action is the most literal, building only the
requested packages and no others. Yet 'grpinst' is still smart enough to call \fBpkgadd\fP(8)
with the '-u' switch, if one of the packages passed as argument is already installed.
.TP
.B remove [\-\-rargs=] <package1> [<package2> ...]
remove packages listed in this order. The only relevant option you can might want to pass to
\fBpkgrm\fP(8) is --root (or -r), used when you want to manage a CRUX installation on a temporarily
mounted filesystem. In order not to confuse the argument parser (which splits on whitespace),
you should format such a request as
.B prt\-auf remove \-\-rargs=\-\-root=/path/to/mounted/crux <package1> [<package2> ...]
and \fBprt\-auf\fP will clean up the -r switch so that \fBpkgrm\fP(8) does what you want.
.TP
.B sysup
Update all installed packages which are outdated.
.TP
.B lock
and
.B unlock
commands to keep the currently installed versions, even if there are
changes in the ports tree.
.TP
.B lock <package1> [<package2>...]
Do not update these packages in a
.B sysup
operation
.TP
.B unlock <package1> [<package2>...]
Remove lock from these packages
.TP
.B listlocked
List names of packages which are locked.
.TP
.B diff [--all]
show differences between installed packages and ports in the ports
tree. Locked packages are only displayed if you use the --all switch.
.TP
.B quickdiff
prints a simple list of packages which have a different version in the
ports tree than what is installed.
.TP
.B search [\-\-path] [\-\-regex] <expr>
Search the ports tree for
.B expr
in their name
.TP
.B dsearch [\-\-path] [\-\-regex] <expr>
Search the ports tree (both name and description) for the pattern
\fBexpr\fP. The search in the description is not case sensitive. Note that
this requires prt\-auf to read every Pkgfile, which makes it rather slow; if
you like this, consider using the cache functionality, so you only have to
spend this time once after updating the ports tree has been updated.
.TP
.B fsearch [\-\-path] [\-\-regex] <pattern>
Search the ports tree for file names that match \fBpattern\fP.
Pattern should be a Perl-compatible regular expression (e.g. prt-auf fsearch
--regex 'liblz(o2|ma).*') unless it contains no metacharacters (such as: +,
*, ., / ), in which case you can omit the \-\-regex switch.
.TP
.B info <port>
Print available info for a port
.TP
.B path <port>
Print the path of a port
.TP
.B readme <port>
Print the port's README file if it exists
.TP
.B depends <package1> [<package2> ...]
print a recursive list of dependencies needed to install the packages passed
as argument. It shows a list of the dependencies that were found in the
ports tree, plus a list of the dependencies that could not be found.
.TP
.B quickdep <package1> [<package2> ...]
print a simple list of recursive dependencies for all the packages passed
as argument. The output is formatted to be useful in command substitution,
e.g. instead of running
.B prt\-auf depinst xorg-server
you might obfuscate your intentions with a gratuitous loop:
.B for i in $(prt\-auf quickdep xorg-server); do prt\-auf isinst $i || prt\-auf install $i; done
Note: output is restricted to those dependencies that can be found in the ports tree. It might be
useful to run
.B prt\-auf depends <package1> | grep \(dq\-\- missing packages\(dq
as a first step, in order to ensure that your ports tree has everything needed for successful
builds.
.TP
.B deptree <package>
print a tree of the dependencies of the package
.B package.
.SH ""
Note that soft (optional) dependencies are NOT considered when running
prt\-auf depends, prt\-auf quickdep, or prt\-auf deptree. The port maintainer
often provides a README if significant loss of functionality might result from
not having an optional dependency present when building, so be sure to interpret the output of
.B prt\-auf <depends|quickdep|deptree>
in light of the information provided in such a README.
.TP
.B dependent <package>
print a list of ports which have
.B package
in their "Depends on:" line. As with
.B depends, quickdep, deptree,
the Pkgfile line for soft (optional) dependencies is NOT parsed during this operation,
so the output might omit some of the ports that were linked against
.B package
during compilation.
By default, output is restricted to ports that are installed. To see all hard dependencies,
add the --all switch; use --recursive to get a recursive list (without duplication),
and --tree to get a nicely indented one (note that --tree implies
--recursive).
.TP
.B dup
List ports which can be found in multiple directories configured in
.B /etc/prt-get.conf
.TP
.B list [\-v|\-vv]
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 listinst [\-v|\-vv]
List installed ports. It's basically the same as
.B pkginfo \-i,
but omits version when called without verbose (\-v, \-vv) switch. Plus
it is notably faster in my tests. \-v adds version information, \-vv
adds version and description.
.TP
.B listorphans
List installed ports which do not appear in the "Depends on:" line of
any other port currently installed. Output appears alphabetically separated by newlines, making it
suitable for process substitution as shown in the
.B EXAMPLES
section below. Note that some core ports might be runtime
dependencies despite their absence in the "Depends on:" line; see \fBPkgfile(5)\fP
for an explanation of this practice. Also remember that this operation does NOT account for soft
(optional) dependencies. Removing a non-core package returned by this command might require a
rebuild of other packages; use revdep(1) to locate such breakage.
.TP
.B isinst <package1> [<package2> ...]
Check whether each package given on the command line is installed. Output in the case of multiple
arguments is separated by newlines, suitable for processing by awk or grep. Similar to
.B pkginfo \-i|grep \-E '^(package1|package2|...)'
but does not print the version information. This command has a return value of 0 if
all packages given as argument are installed, otherwise a return value greater than 0.
.TP
.B current <package1> [<package2> ...]
Shows the currently-installed version of <package1>, or a message
that <package1> is not installed. Also takes more than one package as
argument.
.TP
.B ls [--path] <package>
Prints out a listing of the port's directory
.TP
.B cat <package> [<file>]
Prints out the file to stdout. If <file> is not specified, 'Pkgfile' is used. If set, uses $PAGER.
.TP
.B edit <package> [<file>]
Edit the file using the editor specified in the $EDITOR environment variable.
If <file> is not specified, 'Pkgfile' is used.
.TP
.B help
Shows a help screen
.TP
.B version
Shows the current version of prt\-auf
.TP
.B cache
create a cache file from the ports tree, which will be used whenever \fBprt\-auf\fP
is invoked with the --cache option. Remember to run \fBprt\-auf cache\fP each time
you update the ports tree, or automate this step by appending a line to the
\fBports\fP(8) script. If you invoke \fBprt\-auf\fP from a symbolic link that ends
in 'cache', \fBprt\-auf\fP will act as if it saw the --cache option on the command
line, so the symbolic link \fBprt\-cache\fP -> \fBprt\-auf\fP will save you the
hassle of typing '--cache' each time. Cache files generated by \fBprt\-auf\fP are
fully compatible with those generated by \fBprt\-get\fP(8).
.SH "OPTIONS"
The following options are primarily useful for install/update transactions.
.TP
.B -fr
Force rebuild, Implies 'pkgmk -f'; same as --margs=-f
.TP
.B -us
Update signature, implies 'pkgmk -us'; same as --margs=-us
.TP
.B -is
Ignore signature, implies 'pkgmk -is'; same as --margs=-is
.TP
.B -uf
Update footprint, implies 'pkgmk -uf'; same as --margs=-uf
.TP
.B -if
Ignore footprint, implies 'pkgmk -if'; same as --margs=-if
.TP
.B -ns
No stripping, implies 'pkgmk -ns'; same as --margs=-ns
.TP
.B -kw
Keep working directory, implies 'pkgmk -kw'; same as --margs=-kw
.TP
.B \-\-margs="...", e.g. \-\-margs="\-im"
additional arguments to be passed to pkgmk;
note that \-d is already passed to pkgmk anyway
.TP
.B \-\-aargs="...", e.g. \-\-aargs="\-f"
additional arguments to be passed to pkgadd
.TP
.B \-\-cache
Use cache file for this command.
.SH ""
The following options affect the output of non-install (information-seeking) transactions.
.TP
.B \-v, \-vv
(verbosity level) Show version of a port (\-v), or show both version
and description (\-vv). Passing more than one of these options is equivalent to \-vv.
.TP
.B \-\-path
Show path info for the ports found by a search or a dependency calculation
.TP
.B \-\-regex
Interpret filter and search pattern as regular expression
.SH "CONFIGURATION"
Most of the directives available in prt\-get.conf(5) are also recognized and
respected by \fBprt\-auf\fP. Notably, you can specify the active port
collections by ensuring that they appear on lines beginning with 'prtdir '.
You can also toggle the running of pre-/post-install scripts by editing the
line that contains 'runscripts'. You can specify alternatives to the
default pkgutils programs ( /usr/bin/pkgmk, /usr/bin/pkgadd, and
/usr/bin/pkgrm )
by editing the lines for 'makecommand', 'addcommand', and 'removecommand', respectively.
.SH "TECHNICAL DETAILS"
\fBprt\-auf\fP aims to recreate the familiar experience of \fBprt\-get\fP(8), in a
tidy Perl program that novice CRUX hackers would find less intimidating. By keeping its
inner workings entirely within one file, \fBprt\-auf\fP makes it easier for CRUX newcomers
to understand the architecture of the \fBports\fP(8) system and the \fBpkgutils\fP.
One intended consequence of the less-intimidating code base is that bug reports and
feature requests can receive the attention of more CRUX users, rather than just the
handful of developers who have C++ experience. In order to facilitate the insertion
of new code to satisfy any feature requests, this section provides an outline of the
\fBprt\-auf\fP design.
The program begins by declaring all the variables that are shared among subroutines.
Some of these variables are initialized right away, but other variables are only
initialized once the program knows the requested action.
After all the arguments are parsed (and screened for validity), the hash maps \fI%opkg\fP,
\fI%odepends\fP, and \fI%osearch\fP will retain in memory the user's desired settings. Then the
relevant data structures are populated from the files on disk (the cache, if
\-\-cache was passed on the command line, the database of installed packages in /var/lib/pkg,
the list of locked ports, the list of aliases, or each \fBPkgfile\fP(5) found in the ports tree).
Control is now passed to the subroutine that satisfies the given request. Many of these
subroutines return a simple array of strings, most notably the subroutions \fIlist_ports()\fP,
\fIdeporder()\fP, and \fIport_diff()\fP. But the \fIup_inst()\fP subroutine returns references
to five different arrays, so that post-processing can provide informative output regarding which
ports were successfully installed, and which ports failed.
The final section of the main program (post-processing) considers the distinctive output of each
subroutine and customizes the handling of the \fI@results\fP array accordingly. This section is
also where the verbose switch (-v|-vv) is taken into account, appending to each element of
\fI@results\fP the version or description of the ports found in the search.
Here are some of the deliberate deviations from the behaviour of \fBprt\-get\fP(8), the first of
which was on the TODO list in the prt-get source tree.
.PP
.TP
\ \ \ \(bu mixed install/update mode. Packages given on the command line can be present or not, and
\fBprt\-auf\fP will figure out the right way to call \fBpkgadd\fP(8) for each one.
.TP
\ \ \ \(bu merged install and depinst. While they still retain some distinguishability in how they
treat a given set of targets, both of them now resolve dependencies by default. The old
behaviour of \fBprt\-get install\fP can be approximated by the 'grpinst' action of \fBprt\-auf\fP.
.TP
\ \ \ \(bu logging is not handled internally. In the event that stdout is \fInot\fP redirected to a
file, consider using the 'grpinst' action so that error messages remain in the scrollback buffer.
If \fBprt\-auf\fP is used non-interactively (say, in a cron job), then another non-interactive
process can take care of renaming the file where the stdout of \fBprt\-auf\fP was dumped. This
design decision is closer to the spirit of "do one thing and do it well", although \fBprt\-auf\fP
ignores this advice elsewhere by implementing such luxuries as
.B prt\-auf ls, prt\-auf edit, prt\-auf readme
when command substitutions like
.B ls $(prt\-auf path $desired_port)
or
.B vim $(prt\-auf path $desired_port)/Pkgfile
are perfectly cromulent ways to do the same thing.
.SH "EXAMPLES"
.TP
.B prt\-auf install irssi
Download, build and install irssi, with one simple command
.TP
.B prt\-auf install paper yasm
Install paper and yasm (and any needed dependencies).
.TP
.B prt\-auf update bmake cmake
Update bmake and cmake. Abort with an informative error message if either package is not yet
installed, allowing you to issue a revised command.
.TP
.B prt\-auf update -fr openssh
Update your current version of openssh, forcing a rebuild even if no version
difference is detected. Useful if there was a major version change in one of
its dependencies, and \fBrevdep openssh\fP indicates a broken package. :\-)
.TP
.B prt\-auf info glib-networking
Show info about glib-networking
.TP
.B MISSLIBS=$(revdep -vvv mpv | awk -F ':' '/(missing library)/ {print $3}'); [ -n \(dq${MISSLIBS[@]}\(dq ] && for i in ${MISSLIBS[@]}; do prt\-auf fsearch $i; done
(adapted from a script by ppetrov^) Check for the presence of the runtime libraries needed by mpv.
If any are absent, search the footprints to determine which ports provide the missing libraries.
.TP
.B prt\-auf dsearch irc
Return a list of all ports having "irc" in their name or description
.TP
.B comm -13 <(ls /usr/ports/core) <(prt-auf listorphans)
(based on comments from Romster and jue) Filter out the core ports from the list of orphans, in
shells (like bash) that support process substitution
.TP
.B comm -13 <(cat ~/.keepers <(ls /usr/ports/core) | sort) <(prt\-auf listorphans) | xargs prt\-auf remove
(system-hosing extension of the above) A one-liner inspired by \fBpkg\-clean\fP
and \fBpkgfoster\fP, but without the safeguard of interactivity. \fBDo not try this on a
mission-critical system.\fP
.TP
.B prt\-auf isinst $(prt\-auf quickdep $(prt\-auf quickdiff)) | awk '/not installed/ {print $2}'
(adapted from a comment by Fun) After updating your ports tree, print out a list of dependencies
that were not needed the last time you built your currently-installed ports, but are needed now by
the newer versions of these ports. The output of this command is sorted by dependencies, therefore
suitable for piping to \fBxargs prt\-auf install\fP or \fBxargs prt\-auf grpinst\fP.
.TP
.B prt\-auf grpinst $(prt\-auf quickdep graphviz)
Installed all packages needed for graphviz . Remember that grpinst stops
installing when one package installation fails.
.TP
.B prt\-auf listinst | xargs prt\-auf depends | xargs prt-auf grpinst \-\-aargs="\-r=/mnt"
Sort the list of installed packages by dependencies, and then install all
those packages onto a backup filesystem (mounted at /mnt). If you have a
customized pkgadd.conf that you want applied to this operation, either copy
it to /mnt/etc where pkgadd will be looking for it, or pass the additional
option \-\-aargs=\(dq\-c /etc/pkgadd.conf\(dq to the grpinst command.
.TP
.B prt\-auf list --path --regex '^xorg.*' | grep -v "/usr/ports/xorg"
Show the ports whose names begin with xorg, but which appear outside the xorg port collection.
(At the time of writing, this command returned at least two font ports.)
.SH "AUTHORS"
John McQuah <jmcquah at disroot dot org>, based on the prt\-get manpage by
Johannes Winkelmann, and other sources cited inline.
.SH "SEE ALSO"
prt\-get.conf(5), Pkgfile(5), pkgmk(8), pkgadd(8), pkgrm(8), ports(8)

813
scripts/prt-auf Executable file
View File

@ -0,0 +1,813 @@
#!/usr/bin/perl
#
# prt-auf --- add/update frontend to CRUX pkgutils (offers mostly the same
# user experience as prt-get except for the slight delay
# entailed by Perl having to compile this file on startup)
#
# distributed under the same license as the pkgutils,
# https://crux.nu/gitweb/?p=tools/pkgutils.git;a=blob_plain;f=COPYING;hb=HEAD
#
use warnings;
use strict;
################### Initialize global variables #######################
my $title="prt-auf"; my $version=0.5;
my $CONFDIR = "/var/lib/pkg"; my $prtalias="/etc/prt-get.aliases";
my $pkgdb="$CONFDIR/db"; my $prtlocker="$CONFDIR/prt-get.locker";
my $prtcache="$CONFDIR/prt-get.cache"; my $LOCKED; my %ALIASES; my %DEPENDS;
my @allports; my %V_REPO; my %V_INST; my %DESC;
my @results; my $strf; my $ind; my $hh; my $portpath; my $built_pkg;
my %osearch = ( cache => 0, regex => 0, path => 0, exact => 0, verbose => 0 );
my %odepends = ( tree => 0, recursive => 0, all => 0 );
my %opkg = ( margs => "", aargs => "", rargs => "", runscripts => "yes",
makecommand => "/usr/bin/pkgmk", addcommand => "/usr/bin/pkgadd",
removecommand => "/usr/bin/pkgrm", test => "no" );
my @bldirs = parse_prt_conf();
my @basedirs = @{$bldirs[0]}; my @localports = @{$bldirs[1]};
################### Process the given command #########################
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();
}
if ($osearch{cache}==1) { fill_hashes_from_cache(); }
if ($action !~ /^(quickdep|search|dsearch|fsearch|info|dup|readme|cat)$/) {
open (DB, $pkgdb) or die "Could not open package database!\n";
local $/="";
while (<DB>) { $V_INST{$1} = $2 if m/^(.*)\n(.*)-[0-9]+\n/; }
close (DB);
}
if ($action =~ /^(diff|quickdiff|depends|deptree|install|update|depinst|sysup)$/) {
get_locked_and_aliased();
}
############## 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);
@results = get_pkgfile_fields($portpath,"all") if ($portpath);
} elsif ($action eq "cache") { dump_flat_db();
} 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 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 "help") { print_help();
} elsif ($action eq "version") { print "$title $version\n";
} else { printf "Unsupported command '$action'.\n"; }
#################### Post-transaction reports #######################
$strf = "%s\n";
if (($action =~ /^(listinst|listorphans)/)
or (($action eq "dependent") and ($odepends{all}==0))) {
foreach my $result (@results) {
$result .= " $V_INST{$result}" if $osearch{verbose}==1;
$result .= " $V_INST{$result}\n$DESC{$result}\n" if $osearch{verbose}>1;
printf "$strf", $result;
}
} elsif ($action =~ /^(list|search|dsearch|path|dependent)/) {
exit 1 if ($#results < 0);
foreach my $result (@results) {
$result =~ s/.*\/(.*)$/$1/ if (($action ne "path") and ($osearch{path}==0));
$result .= " $V_REPO{$result}" if $osearch{verbose}==1;
$result .= " $V_REPO{$result}\n$DESC{$result}\n" if $osearch{verbose}>1;
printf $strf, $result;
}
} elsif ($action =~ /^(fsearch)/) {
my %hits = %{$hh}; $strf = "%20s %s\n";
printf $strf, "Found in", "Matching File";
foreach my $fh (keys %hits) {
chomp($hits{$fh});
printf $strf, $fh, (split /\s/, $hits{$fh})[-1];
}
} elsif ($action =~ /^(diff|quickdiff|current|isinst)$/) {
exit $ind;
} elsif ($action =~ /^(depends|deptree|quickdep)$/) {
print "-- dependencies ([i] = installed, [a] = provided by an alias)\n" if ($action =~ /^dep/);
my $indent=($action eq "deptree") ? " " : "";
my @installed=keys %V_INST unless ($action eq "quickdep");
my %seen; my $strf="%3s %s\n"; my $depline; my $dep; my $missing=0;
foreach $depline (@results) {
if ($depline =~ /MISSING/) { $missing=1; print "-- missing packages\n"; next; }
my $cleandep = $depline;
$cleandep =~ s/ .provided by .*// if ($action eq "deptree");
$dep = (split / /, $cleandep)[-1];
next if (($seen{$dep}) and ($odepends{all}==0));
$seen{$dep}=1;
if ($action ne "quickdep") {
$ind = (grep { $_ eq $dep } @installed) ? "[i]" : "[ ]";
if ($ind ne "[i]") {
$ind = (who_aliased_to($dep)) ? "[a]" : $ind;
}
}
$depline .= " $V_REPO{$dep}" if $osearch{verbose}==1;
$depline .= " $V_REPO{$dep}\n$DESC{$dep}" if $osearch{verbose}>1;
printf "$strf", $ind, $depline unless ($action eq "quickdep");
printf "%s ", $dep if ($action eq "quickdep");
}
print "\n" if ($action eq "quickdep");
} elsif ($action eq "info") {
$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];
}
printf $strf, $fields[8], $results[8];
} elsif ($action eq "remove") {
my %removed = %$ind;
my @successes = grep { $removed{$_}==1 } keys %removed;
my @failures = grep { $removed{$_}==0 } keys %removed;
print "Ports removed:\n" if (@successes);
foreach my $p (@successes) { print "$p\n"; }
} elsif ($action =~ /^(install|update|depinst|grpinst)$/) {
my @ok = @{$results[0]}; my @ok_pre = @{$results[1]}; my @ok_post = @{$results[2]};
my @ok_readme = @{$results[3]}; my @not_ok = @{$results[4]}; my $note;
if (($opkg{test} eq "no") and (@ok)) {
print "Successful ports:\n";
foreach my $k (@ok) {
$note = (grep /^$k$/, @ok_pre) ? " pre: ok. " : "";
$note .= (grep /^$k$/, @ok_post) ? " post: ok. " : "";
$note = ((grep /^$k$/, @ok_pre) or (grep /^$k$/, @ok_post))? "($note)" : "";
print " $k $note\n";
}
print "\n";
}
if (($opkg{test} eq "no") and (@ok_readme)) {
print "Successful ports with README files:\n";
foreach (@ok_readme) { print " $_\n"; }
print "\n";
}
if (($opkg{test} eq "no") and (@not_ok)) {
print "Ports with pkgmk/pkgadd failures:\n";
foreach (@not_ok) { print " $_\n"; }
print "\n";
}
} else {}
# Done!
#################### Begin Subroutines #######################
sub parse_args {
my @query;
$osearch{cache} = 1 if ($0 =~ /cache$/);
while (my $arg = shift) {
if ($arg =~ /^(search|dsearch|fsearch|path|info|list|dup)$/) { $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 =~ /^(depends|deptree|quickdep|dependent)$/) { $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 "--path") { $osearch{path} = 1;
} elsif ($arg eq "--regex") { $osearch{regex} = 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;
} elsif ($arg =~ /^--margs=(.*)/) { $opkg{margs} .= $1;
} elsif ($arg =~ /^--aargs=(-r|--root)=(.*)/) { $opkg{aargs} .= "$1 $2";
} elsif ($arg =~ /^--rargs=(-r|--root)=(.*)/) { $opkg{rargs} .= "$1 $2";
} elsif ($arg =~ /^-/) {
print "'$arg' is not a recognized option.\n"; exit 1;
} else { push (@query, $arg); }
}
if (($#query > -1) and
($action =~ /^(diff|quickdiff|cache|list|dup|sysup)/)) {
print "warning: $1 takes no arguments; ignoring those given.\n";
}
if (($#query > 0) and
($action =~ /^(search|dsearch|fsearch|info|readme|path|ls)$/)) {
print "warning: $1 takes only one argument; ignoring all but the last.\n";
}
if ((! @query) and
($action =~ /^(search|dsearch|fsearch|info|readme|path|ls)$/)) {
print "$1 requires an argument.\n"; exit 1;
}
if (($#query != 0) and
($action =~ /^(deptree|dependent)$/)) {
print "$1 requires exactly one argument.\n"; exit 1;
}
if (($#query < 0) and
($action =~ /^(install|update|depinst|grpinst|remove)$/)) {
print "$1 requires at least one argument.\n"; exit 1;
}
return $action, @query;
}
sub parse_prt_conf {
my @basedirs; my @localports; my $runscripts; my $make; my $add; my $remove;
my $conf = "/etc/prt-get.conf";
open(PORTS, $conf) or die "could not open $conf";
while (<PORTS>) { chomp;
if ( /^prtdir\s+/ ) {
my $line = $_;
$line =~ s/^prtdir\s+//; #remove the leading directive
$line =~ s/#.*$//; #strip inline comments like this one
$line =~ s/\s+//g; #collapse all whitespace, even if in a path!
if ( $line !~ /:/ ) {
push @basedirs, $line if (-d $line);
} else {
my @a = split(/:/, $line);
my @b = split(/,/, $a[1]);
while ( my $c = pop @b ) {
my $port = $a[0] . "/" . $c;
push @localports, $port if (-d $port);
}
}
}
$opkg{runscripts} = $1 if /^runscripts\s+(.*)#/;
$opkg{makecommand} = $1 if /^makecommand\s+(.*)#/;
$opkg{addcommand} = $1 if /^addcommand\s+(.*)#/;
$opkg{removecommand} =$1 if /^removecommand\s+(.*)#/;
}
close(PORTS);
return \@basedirs, \@localports;
}
sub find_dups {
my %seen;
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"; }
}
}
}
sub get_locked_and_aliased {
local $/ = "\n";
open (AL, $prtalias) or return;
while (<AL>) { $ALIASES{$1} = $2 if m/^\s*(.*)\s*:\s*(.*)/; }
close (AL);
open (LK, $prtlocker) or return;
while (<LK>) { $LOCKED .= " $_"; }
close (LK);
}
sub who_aliased_to {
my $target = shift;
my @substitutes = grep { defined $V_INST{$_} } keys %ALIASES;
@substitutes = grep { $ALIASES{$_} eq $target } @substitutes;
my $who = (@substitutes) ? $substitutes[0] : undef ;
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];
}
} close (CACHE);
print "cache created.\n";
}
sub fill_hashes_from_cache {
open (my $cf,$prtcache) or die "cannot use $prtcache as a cache!\n";
local $/="\n"; my $p; my $deps;
my $ignored=<$cf>; # first line only contains the cache format version
while (1) {
$p = <$cf>; last unless defined $p;
chomp($p);
$ignored = <$cf>; $V_REPO{$p} = <$cf>;
$ignored = <$cf>; $DESC{$p} = <$cf>;
$deps = <$cf>;
chomp($deps, $DESC{$p}, $V_REPO{$p});
$DEPENDS{$p} = ($deps ne "") ? $deps : " ";
$DEPENDS{$p} =~ s/,/ /g;
for (my $i=6; $i<13; $i++) { $ignored = <$cf>; }
}
close ($cf);
}
sub fill_hashes_from_pkgfiles {
foreach my $pp (@allports) {
my $p = (split /\//, $pp)[-1];
my ($rver, $rrel, $rdesc, $rdeps) = get_pkgfile_fields($pp);
$V_REPO{$p} = ($rver) ? $rver : "0";
$DEPENDS{$p} = ($rdeps) ? $rdeps : "";
$DEPENDS{$p} =~ s/,/ /g;
$DESC{$p} = ($rdesc) ? $rdesc : "";
}
}
sub get_pkgfile_fields {
my ($descrip, $url, $maintainer, $packager, $Version, $Release)=('','','','','',0,0);
my ($readme, $preInstall, $postInstall, $Dependencies)=("no","no","no",'');
my $portpath = shift; my $Name = (split /\//, $portpath)[-1];
my $pkgfile = "$portpath/Pkgfile";
$readme = "yes" if (-f "$portpath/README") or (-f "$portpath/README.md");
$preInstall = "yes" if (-f "$portpath/pre-install");
$postInstall = "yes" if (-f "$portpath/post-install");
$portpath =~ s/\/[a-zA-Z0-9]+$//;
open(PF,$pkgfile) or die "Cannot open $pkgfile for reading!\n";
while (<PF>) {
chomp;
if (s/^# Description:\s*(.*)/$1/) { $descrip = $_; }
elsif (s/^# URL:\s*(.*)/$1/) { $url = $_; }
elsif (s/^version=(.*)/$1/) { $Version = $_; }
elsif (s/^release=(.*)/$1/) { $Release = $_; }
elsif (s/^# Depends on:\s*(.*)/$1/) { $Dependencies = $_; }
elsif (s/^# Packager:\s*(.*)/$1/) { $packager = $_; }
elsif (s/^# Maintainer:\s*(.*)/$1/) { $maintainer = $_; }
else {}
} close(PF);
if (shift) {
return $Name, $portpath, $Version, $Release, $descrip, $Dependencies, $url,
$packager, $maintainer, $readme, $preInstall, $postInstall, $built_pkg;
} else { return $Version, $Release, $descrip, $Dependencies; }
}
sub find_port_by_file { # for now only used to search footprints, but can be generalized
my $portfile = shift; my $query = shift; my ($lp, $candidate, $fh); my %hits=();
my $linewanted = qr/$query/is;
LOCALENTRY: foreach $lp (@localports) {
open ($fh, "$lp/$portfile") or die "cannot open $portfile for $lp\n";
while (<$fh>) {
next if ($portfile eq "Pkgfile" and $_ !~ /^(name=|# Description)/);
next LOCALENTRY if $hits{$lp};
$hits{$lp} = $_ if $_ =~ $linewanted;
} close ($fh);
}
foreach my $collection (@basedirs) {
my $prefix = ( $osearch{path} == 1 ) ? "$collection/" : "";
opendir (DIR, $collection) or return;
PORTENTRY: foreach $candidate (sort(readdir(DIR))) {
next if (! -f "$collection/$candidate/$portfile");
open ($fh, "$collection/$candidate/$portfile") or die "cannot open $portfile in $candidate\n";
while (<$fh>) {
next PORTENTRY if $hits{"$prefix$candidate"};
$hits{"$prefix$candidate"}=$_ if $_ =~ $linewanted;
} close ($fh);
} closedir(DIR);
}
return \%hits;
}
sub find_port_by_desc {
my $query=shift;
$query =~ s/\+/\\\+/g unless ($osearch{regex}==1);
$query =~ s/\./\\\./g unless ($osearch{regex}==1);
my @hits = grep { $DESC{$_} =~ /$query/i } keys %DESC;
return @hits;
}
sub find_port_by_name {
my $query = shift; my $exact=shift; my $fullpath=shift; my $exhaustive=shift;
$query =~ s/\+/\\\+/g unless ($osearch{regex}==1);
$query =~ s/\./\\\./g unless ($osearch{regex}==1);
my $pattern = ($exact==1) ? qr/\/$query$/s : qr/$query/is;
my @hits = grep { $_ =~ $pattern } @allports;
@hits = grep { s/^(.*)\/// } @hits if ($fullpath==0);
return @hits if ($exhaustive==1);
return $hits[0] if ($exhaustive==0);
}
sub uninstall {
my $PKGRM = $opkg{removecommand};
my @targets = grep { defined $V_INST{$_} } @_;
my @rubbish = grep { ! defined $V_INST{$_} } @_;
foreach my $r (@rubbish) { print "$r not installed; ignoring.\n"; }
my %removed = map { $_ => 0 } @targets;
foreach my $t (@targets) {
($opkg{test} eq "no") ? system($PKGRM,$opkg{rargs},$t) : print "$PKGRM $opkg{rargs} $t\n";
$removed{$t}=1 if ($?>>8 == 0);
}
return \%removed;
}
sub port_lock {
my @newlocks = grep { " $LOCKED " !~ / $_ / } @_;
if (@newlocks) {
open (LK,'>>',$prtlocker) or die "cannot open $prtlocker for writing.\n";
foreach my $lp (@newlocks) {
print LK "$lp\n";
print STDOUT "$lp locked.\n";
} close (LK);
}
}
sub port_unlock {
foreach my $ul (@_) { $LOCKED =~ s/ $ul / /; }
my @newlocks = split / /, $LOCKED;
open (LL, $prtlocker."-tmp",">");
foreach my $nl (@newlocks) { print LL "$nl\n"; }
close (LL);
system ("mv",$prtlocker."-tmp",$prtlocker);
}
sub list_ports {
my @found; my $filter = shift;
our $indent="0 "; our $height=0; our @descendants=(); our @outfile;
if (! $filter) { # empty arg: list all valid ports
foreach my $collection (@basedirs) {
opendir (DIR, $collection) or next;
foreach my $port (sort(readdir DIR)) {
next if (! -f "$collection/$port/Pkgfile");
push (@found, "$collection/$port");
} closedir (DIR);
}
foreach my $lp (@localports) {
push (@found, $lp) if (-f "$lp/Pkgfile");
}
} elsif ($filter eq "inst") { @found = keys %V_INST;
} elsif ($filter eq "locked") { @found=split / /, $LOCKED;
} elsif ($filter =~ /^(orphans|dependent)$/) {
my $seed=shift;
if ( ((! $seed) and ($filter eq "dependent")) or
((shift @_) and ($filter eq "dependent")) ) {
print "dependent requires exactly one argument.\n";
return;
}
if (($filter 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)) ?
keys %V_INST : keys %DEPENDS;
@searchspace = grep { defined $DEPENDS{$_} } @searchspace;
if ($filter 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)) {
@found = grep { " $DEPENDS{$_} " =~ / $seed / } @searchspace;
} elsif (($filter eq "dependent") and ($odepends{recursive}==1)) {
push @outfile, "$seed";
my @children = grep { " $DEPENDS{$_} " =~ / $seed / } @searchspace;
foreach my $sd (@children) { recurse_offtree($sd); }
sub recurse_offtree {
my $s = shift; push @outfile, (${indent}x(1+$height))."$s";
my @offspring = grep { " $DEPENDS{$_} " =~ / $s / } @searchspace;
foreach my $dc (@offspring) {
if (grep /^$dc$/, @descendants) {
print "Warning: cyclic dependencies found!\n";
return;
}
push (@descendants, $dc);
$height = 1+$#descendants;
recurse_offtree($dc);
pop(@descendants);
$height = 1+$#descendants;
}
}
my %seen;
@outfile = grep { !m/^\s*$/ } @outfile;
@outfile = sort(@outfile) unless ($odepends{tree}==1);
@found = ($odepends{tree}==1) ? grep { s/0 / /g } @outfile :
grep !$seen{$_}++, grep { s/0 //g } @outfile;
unshift (@found, $seed);
}
# possibilities for the recursive switch have been exhausted
else { }
} # possibilities for the filter have been exhausted
else { }
return @found;
}
sub port_diff { # returns a scalar indicating how many differences were found
my $dtype=shift; my $lastcol;
my @argq=@_; my $retval=0; my $format="%30s %20s %20s\n";
if ($dtype !~ /^(current|isinst|utd)/) {
printf "$format", "Port", "Installed", "In Repository" if (! $dtype);
foreach my $p (keys %V_INST) {
next if ((" $LOCKED " =~ / $p /) and ($odepends{all}==0));
$lastcol = ($V_REPO{$p}) ? $V_REPO{$p} : "MISSING!";
if ($lastcol ne $V_INST{$p}) {
printf "$format", $p, $V_INST{$p}, $lastcol if (! $dtype);
printf "%s ", $p if ($dtype eq "quick" and $lastcol ne "MISSING!");
}
}
printf "\n" if ($dtype eq "quick");
} elsif ($dtype eq "utd") {
while (my $q=shift(@argq) and $retval==0) {
$retval-- if (! $V_INST{$q});
$retval++ if (($V_INST{$q}) and ($V_REPO{$q}) and ($V_INST{$q} ne $V_REPO{$q}));
}
} elsif ($dtype =~ /^(current|isinst)$/) {
foreach my $q (@argq) {
if (! $V_INST{$q}) {
print "$q: not installed\n"; $retval++;
} else {
print "$q: version $V_INST{$q}\n" if ($dtype eq "current");
print "$q is installed.\n" if ($dtype eq "isinst");
}
}
} else {}
return $retval;
}
sub deporder {
# returns an indented list if called with first arg "deptree",
# otherwise returns a flattened list, pruned of duplicates.
# Recursion does NOT continue beyond a dependency satisfied by an alias.
my $format=shift; our $indent="0 "; our $height=0; our @seeds = @_;
our @ancestry=(); our @outfile=(); our @missing; my %installable; my %seen;
foreach my $s (@seeds) {
if (find_port_by_name($s,1,0,0)) { $installable{$s} = 1;
} else { $installable{$s} = 0;
print "$s not found in the ports tree; ignoring.\n";
}
}
foreach my $s (grep { $installable{$_}==1 } @seeds) {
recurse_deptree($s);
}
sub recurse_deptree {
my $s = shift;
if (! grep /$s$/, @allports) { push @missing, "$s"; return; }
my $substitute = who_aliased_to($s);
my $note = ($substitute) ? " (provided by $substitute)" : "";
push @outfile, (${indent}x(1+$height))."$s$note";
my $depstr = $DEPENDS{$s} unless ($substitute);
if ($depstr) {
my @sdeps = split /[ ,]/, $depstr;
foreach my $sd (@sdeps) {
if (grep /^$sd$/, @ancestry) {
print "Warning: cyclic dependency found!\n";
print ((join " => ", @ancestry)."$sd\n");
return;
}
push (@ancestry,$sd);
$height = 1+$#ancestry;
recurse_deptree($sd);
pop @ancestry;
$height = 1+$#ancestry;
}
}
}
if ($format eq "deptree") {
@outfile = grep { s/0 / /g; !m/^\s*$/; } @outfile;
} else {
@outfile = grep !$seen{$_}++,
(grep { s/0 //g; s/ .provided by .*//g; !m/^\s*$/; } sort(@outfile));
}
return @outfile unless ($#missing >= 0);
return @outfile, "MISSING", @missing if ($#missing >= 0);
}
sub up_inst { # returns scalar references to five arrays;
my $type=shift; my @requested=@_;
my $PKGMK=$opkg{makecommand}; my $PKGADD=$opkg{addcommand};
my $SUDO="/usr/bin/doas"; my $FAKEROOT="/usr/bin/fakeroot";
$SUDO = (-x $SUDO) ? $SUDO : "/usr/bin/sudo";
$SUDO = (-x $SUDO) ? $SUDO : "/usr/bin/su -c";
$FAKEROOT = (-x $FAKEROOT) ? $FAKEROOT : "/usr/bin/su -c";
# prepend commands with sudo/doas/fakeroot if the effective user id is not root
if ($> != 0) { $PKGADD = "$SUDO $PKGADD";
$PKGMK = "$FAKEROOT $PKGMK"; }
# resolve all dependencies unless the command was 'grpinst'
my @targets=($type eq "grpinst") ? @_ : deporder("quickdep",@_);
# filter out the invalid ports if deporder() didn't do so already
@targets = grep { (join " ", @allports) =~ / $_ / } @targets if ($type eq "grpinst");
# exempt any locked ports from an sysup operation
if ($action eq "sysup") { @targets = grep {" $LOCKED " !~ / $_ /} @targets;
} else {
@targets = grep {(" $LOCKED " !~ / $_ /) or (grep /$_/, @requested)} @targets;
}
# first determine the directories from which pkgmk must be called
# and the package that will appear after a successful build
my %pdirs; my %builtpkg; my %mkcmd; my %addcmd; my %status;
foreach my $t (@targets) {
$opkg{$t} = $opkg{margs};
$opkg{$t} =~ s/-f// if !(grep /$t/, @requested);
$pdirs{$t} = find_port_by_name($t,1,1,0);
$builtpkg{$t} = find_built_pkg($t);
$mkcmd{$t} = "$PKGMK -d $opkg{$t}";
$addcmd{$t} = "$PKGADD -u $builtpkg{$t}";
$status{$t} = "not done";
}
# build each package, unless already installed or satisfied by an alias
foreach my $t (@targets) {
if (who_aliased_to($t)) {
$mkcmd{$t} = "echo \"skipped build ($t provided by an alias)\"";
} else {
$mkcmd{$t} = "" if ((port_diff("utd",$t)==0) and ($opkg{margs} !~ /-f/));
$mkcmd{$t} = "" if (($type eq "install") and ($V_INST{$t}) and ($opkg{margs} !~ /-f/));
$mkcmd{$t} = "echo \"skipped build ($t up to date)\"" if ((-f $builtpkg{$t}) and
((-M $builtpkg{$t}) < (-M "$pdirs{$t}/Pkgfile")));
}
if ($mkcmd{$t}) {
if ((-f "$pdirs{$t}/pre-install") and ($opkg{runscripts} eq "yes")) {
system("sh","$pdirs{$t}/pre-install") unless ($opkg{test} eq "yes");
$status{$t} += ( $?>>8 == 0 ) ? "pre-install ok. " : "pre-install failed. ";
}
($opkg{test} eq "no") ? chdir $pdirs{$t} : print "cd $pdirs{$t}\n";
($opkg{test} eq "no") ? system("$mkcmd{$t}") : print "$mkcmd{$t}\n";
$status{$t} .= ( $?>>8 == 0 ) ? "build ok. " : "build failed. ";
$status{$t} = ( $mkcmd{$t} =~ /skipped/ ) ? "build skipped. " : $status{$t};
if ($status{$t} =~ /build ok/) {
$addcmd{$t} =~ s/ -u / / if (port_diff("utd",$t)<0);
($opkg{test} eq "no") ? system("$addcmd{$t}") : print "$addcmd{$t}\n";
$status{$t} .= ( $?>>8 == 0 ) ? "pkgadd ok. " : "pkgadd failed. ";
}
if (($status{$t} =~ /pkgadd ok/) and (-f "$pdirs{$t}/post-install")
and ($opkg{runscripts} eq "yes")) {
system("sh","$pdirs{$t}/post-install") unless ($opkg{test} eq "yes");
$status{$t} .= ( $?>>8 == 0 ) ? "post-install ok. " : "post-install failed. ";
}
}
last if (($status{$t} =~ /failed/) and ($type eq "grpinst"));
}
my @ok = grep { $status{$_} =~ /pkgadd ok/ } @targets;
my @ok_pre = grep { $status{$_} =~ /pre-install ok/ } @targets;
my @ok_post = grep { $status{$_} =~ /post-install ok/ } @ok;
my @ok_readme = grep -f $pdirs{$_}."/README", @ok;
my @not_ok = grep { $status{$_} =~ /(pkgadd|build) failed/ } @targets;
return \@ok, \@ok_pre, \@ok_post, \@ok_readme, \@not_ok;
}
sub sysup {
my @targets; my $v_repo;
foreach my $p (keys %V_INST) {
$v_repo = ($V_REPO{$p}) ? $V_REPO{$p} : "MISSING" ;
push @targets, $p if (($v_repo ne $V_INST{$p}) and ($v_repo ne "MISSING"));
}
return up_inst("update",@targets);
}
sub find_built_pkg {
my $target = shift; my $CONF="/etc/pkgmk.conf"; local $/="\n";
my $COMPRESSION; my $PKG_DIR; my $portpath = (find_port_by_name($target,1,1,0))[0];
my ($version, $release) = (get_pkgfile_fields($portpath))[0..1];
open (CF,$CONF) or return;
while (<CF>) {
$COMPRESSION=$1 if m/^PKGMK_COMPRESSION_MODE=(.*)/;
$PKG_DIR=$1 if m/^PKGMK_PACKAGE_DIR=(.*)/;
} close (CF);
if ($COMPRESSION) {
$COMPRESSION =~ s/"//g;
$COMPRESSION =~ s/'//g;
} else { $COMPRESSION = "gz"; }
if ($PKG_DIR) {
$PKG_DIR =~ s/"//g; $PKG_DIR =~ s/'//g;
$PKG_DIR =~ s/\$\{name\}/$target/g;
$PKG_DIR =~ s/\$name/$target/g;
} else { $PKG_DIR = $portpath; }
return "$PKG_DIR/$target#$version-$release.pkg.tar.$COMPRESSION";
}
sub port_ls {
my $port=shift; my $pp=find_port_by_name($port,1,1,0);
return if (! defined $pp);
opendir (DIR,$pp) or die "Cannot open $pp for directory listing!\n";
foreach my $l (sort(readdir(DIR))) {
next if (($l eq ".") or ($l eq ".."));
print "$l\n";
} closedir (DIR);
}
sub port_edit {
my $type=shift; my $port=shift;
my $file=shift; my $pp=find_port_by_name($port,1,1,0);
return if (! defined $pp);
my $EDITOR = ($ENV{EDITOR}) ? $ENV{EDITOR} : "/usr/bin/vi";
if ($type eq "readme") {
port_edit("cat",$pp,"README") if (-f "$pp/README");
port_edit("cat",$pp,"README.md") if (-f "$pp/README.md");
}
if (($file) and (-f "$pp/$file") and ($type eq "edit")) {
exec ($EDITOR,"$pp/$file");
} else { exec ($EDITOR,"$pp/Pkgfile"); }
if (($file) and (-f "$pp/$file") and ($type eq "cat")) {
open (PF, "$pp/$file") or die "Could not open $pp/$file.\n";
while (<PF>) { print $_ ; }
close (PF);
} else {
open (PF, "$pp/Pkgfile") or die "Could not open $pp/Pkgfile.\n";
while (<PF>) { print $_ ; }
close (PF);
}
}
sub print_help {
print <<EOF;
Usage: prt-auf <action> [options] <search term|port name>
where the actions include:
SEARCH
search <expr> show port names containing <expr>
dsearch <expr> show ports containing <expr> in the name or description
fsearch <pattern> show ports that provide filenames matching <pattern>
DIFFERENCES / DEPENDENCIES
quickdiff show outdated packages on a single line, separated by spaces
quickdep <port> show the dependencies needed by <port>, on a single line
deptree <port> show dependency tree for <port>
dependent <port> show installed packages which depend on <port>
INSTALL, UPDATE and REMOVAL
install [opt] <port1 port2...> install ports and their dependencies
update [opt] <port1 port2...> update ports and their dependencies
remove [opt] <port1 port2...> remove ports
GENERAL INFORMATION
list ports in the active repositories
listinst ports currently installed
listorphans installed ports that no other port claims as a hard dependency
dup ports that appear more than once in the active collections
info <port> version, description, dependencies of <port>
path <port> location from which pkgmk would be called to build <port>
ls <port> the files in the <port> directory
cat <port> <file> the contents of <port>/<file> on STDOUT
isinst <port> whether port is installed
current <port> installed version of port
SYSTEM UPDATE
sysup update all outdated ports, except those that are locked
lock <port1 port2...> lock each <port> at its current version
unlock <port1 port2...> release the lock on each <port>
listlocked list locked ports
COMMON OPTIONS
-v show version in listing
-vv show version and decription in listing
--path print path to port if appropriate (search, list, depends)
--cache use a cache file
--test do not actually run pkgmk/pkgadd, just print the commands on STDOUT
EOF
}