Compare commits

...

2 Commits

Author SHA1 Message Date
efbc69ff53 prtsweep.1: initial commit 2022-05-26 17:11:37 -04:00
e1c2785537 prtsweep.pl: initial commit 2022-05-26 17:11:09 -04:00
2 changed files with 319 additions and 0 deletions

107
man1/prtsweep.1 Normal file
View File

@ -0,0 +1,107 @@
.\"
.\" prtsweep manual page.
.\" (C) 2e003 by Martin Opel
.\" Revised 2021 by John McQuah
.\"
.TH prtsweep 1
.SH NAME
prtsweep \- sweep old files from the ports directories
.SH SYNOPSIS
.PP
.B prtsweep
[\-a] [\-d] [\-n] [PORTDIR ...]
.SH DESCRIPTION
The \fIprtsweep\fP perl script sweeps port directories, deleting unneeded files.
"Unneeded" here means not listed in the .signatures file, and not the
built package
.PP
.nf
name#version-release.pkg.tar.?z*
.fi
.PP
All other files are removed. If a traversal of the ports collections in automatic mode
reveals an empty directory (or one of the arguments in [PORTDIR ...] is similarly missing
the required \fBPkgfile\fP), the directory can be removed with the \fI\-d\fP option.
.SH OPTIONS
The following options are supported:
.TP
.I "\-a"
This is the automatic mode. In this mode \fIprtsweep\fP reads the port
collections defined by /etc/ports/*.{git,httpup,rsync} files and cleans all ports
in those collections.
.TP
.I "\-d"
Removes empty directories completely. This happens when ports are moved for
example from opt to contrib or vice versa. Note that this option only
deletes empty directories, so accidently removing whole directory trees
should not happen, even if you use
.IP
.nf
prtsweep \-d /
.fi
.IP
To remove these moved ports completely you can run \fIprtsweep\fP twice:
the first pass removes everything under the directory of the dropped port,
the second pass removes the empty directory.
.TP
.I "\-p"
Also removes the built package.
.TP
.I "\-n"
Dry-run. Do not remove anything really.
.TP
.I "\-q"
Quiet mode. Print messages only for the files that are deleted, not for the
file that are kept. Equivalent to \fBprtsweep [args] | grep -v "keeping"\fP.
.SH ENVIRONMENT
In automatic mode, \fBprtsweep\fP gets a list of active port collections
from /etc/ports/*.{rsync,httpup}, and for each collection descends into the
individual port directories to read the associated .signature files.
After reading .signature, \fBprtsweep\fP will be able to distinguish between
needed sources and unneeded clutter. \fBprtsweep\fP then compares the contents
of the port directory with this list of needed files, and any non-matching
files are deleted (or just printed to stderr if dry-run mode is enabled).
.PP
\fBprtsweep\fP does not distinguish between the current version of the built package
and previous versions. All built packages beginning with the name of the port directory (and
ending in pkg.tar.*) are normally shielded from deletion, except when you pass the option
\fB\-p\fP. Passing this option allows \fBprtsweep\fP to delete every built package,
no matter how old. If you want to keep the current version of the built package but delete
older versions, you can use the companion script \fBprtwash(1)\fP instead.
.PP
After a real cleaning by \fBprtsweep\fP (not dry-run mode), each port
directory should contain only the Pkgfile, source files listed in .signature,
and the built package (if present, and option -p was not given on the command
line).
.SH EXAMPLES
You can call \fIprtsweep\fP with one port:
.PP
.nf
root@hostname:/root # prtsweep /usr/ports/opt/dialog
=======> /usr/ports/opt/dialog
+ removing file dialog-0.9b-20020814.tgz
.fi
.PP
Or you could call the automatic mode, which scans your supfiles in /etc/ports
for all ports directories and cleans them automatically:
.PP
.nf
root@hostname:/root # prtsweep -a
=======> /usr/ports/clc/stable/atop
+ removing file atop-1.8.tar.gz
=======> /usr/ports/clc/stable/coldsync
+ removing file coldsync-2.2.5-gcc3.diff
+ removing file coldsync-2.2.5.tar.gz
=======> /usr/ports/clc/stable/dnsmasq
+ removing file dnsmasq-1.10.tar.gz
...
.fi
.SH AUTHORS
Martin Opel <mo at obbl-net dot de>, John McQuah <jmcquah at disroot dot org>
.SH "SEE ALSO"
prtwash(1)

212
scripts/prtsweep.pl Executable file
View File

@ -0,0 +1,212 @@
#!/usr/bin/perl -w
########################################################################
#
# prtsweep
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# This is a script for removing rubbish from a CRUX port tree.
# Distributed under the terms of the GPL license.
#
# Partial Changelog:
# 1.2.1 - First rewrite in perl
# 1.2 - Added support for renamed sources
#
########################################################################
use strict;
use Cwd qw(cwd getcwd);
use File::Path;
our $version = "1.2.1";
our %options = ( auto => 0, dryrun => 0, rmdir => 0, pkgtoo => 0, quiet => 0 );
our @portdirs;
our $argports;
sub print_usage {
print <<EOT;
Usage: prtsweep [OPTION]... [PORTDIRS]...
-a automatic mode, only valid when [PORTDIRS] are omitted
-d remove directories of dropped ports (no Pkgfile present)
-n dry-run, don't actually delete anything
-p delete any built packages too
-q quiet mode
Report bugs to <jmcquah\@disroot.org>.
EOT
exit(0);
}
sub parse_args {
foreach my $arg (@ARGV) {
if ($arg =~ /^-a$/) {
$options{auto} = 1;
} elsif ($arg =~ /^-d$/) {
$options{rmdir} = 1;
} elsif ($arg =~ /^-n$/) {
$options{dryrun} = 1;
} elsif ($arg =~ /^-p$/) {
$options{pkgtoo} = 1;
} elsif ($arg =~ /^-q$/) {
$options{quiet} = 1;
} elsif ($arg =~ /^--version$/) {
print $version."\n";
exit 0;
} elsif (-d "$arg") {
push (@portdirs, $arg);
} else {
print_usage();
}
}
$argports = @portdirs;
}
sub list_subdirs {
my $path = shift;
my @list;
while ($path =~ s/\/\//\//g) {}
$path =~ s/\/$//;
opendir(DIR, $path) or return;
foreach my $entry(sort(readdir(DIR))) {
next if ( $entry eq "." or $entry eq ".." );
push (@list, "$path/$entry") if -d "$path/$entry"
}
closedir(DIR);
return @list;
}
sub parse_signature {
my @signed = ("Pkgfile",".footprint",".signature","README","README.md",
"pre-install","post-install",".32bit",".nostrip");
my $sigfile = shift;
open (FILE, $sigfile) or return ;
while (<FILE>) {
if (/^SHA256 \(.+\) =.*$/) {
$_ =~ s/^SHA256 \((.+)\) =.*$/$1/;
push (@signed, $_)
}
}
close (FILE);
return @signed ;
}
sub rm_emptydir {
my $portpath = shift;
my $postmsg = ($options{rmdir}==1) ? "\n" : "use '-d' to remove empty directories.\n";
print "$portpath: no Pkgfile found.$postmsg";
return unless $options{rmdir}==1;
my $nf = 0;
opendir (DIR,$portpath) or die "cannot open $portpath for directory listing\n";
foreach (readdir DIR) {
next if (($_ eq '.') or ($_ eq '..'));
$nf += 1;
}
closedir (DIR);
if ($nf == 0) { rmdir $portpath; }
}
sub sweep {
my $port = shift;
while ($port =~ s/\/\//\//g) {}
$port =~ s/\/$//;
my @path = split /\//, $port;
my $builtpkg;
print "=======> $port\n" unless $options{quiet}==1;
my @wanted = parse_signature ("$port/.signature");
$builtpkg=($options{pkgtoo} != 1) ? $path[-1].'#.*pkg\.tar.*' :
'ReallyLongStringThatWouldNeverMatchAnyBuiltPackage';
$builtpkg =~ s/\+/\\\+/;
opendir (DIR, $port) or return;
foreach my $f (sort(readdir(DIR))) {
next if ( $f eq "." or $f eq ".." );
$f =~ s/\+/\\\+/; # plus sign in filenames interferes with regex search
if ((grep /$f/, @wanted) >= 1 or ($f =~ /$builtpkg/)) {
print "... keeping file $port/$f.\n" unless $options{quiet} == 1;
} else {
my $ft=remove_notify("$port/$f");
remove_forreal("$port/$f", $ft) if $options{dryrun} == 0;
}
}
closedir (DIR);
}
sub remove_notify {
my $path=shift;
my $type = (-d $path) ? "directory" : "file";
my $append = ($options{dryrun}==1) ? "(dry run)\n" : "\n";
print "+ removing $type $path $append";
return $type;
}
sub remove_forreal {
my $path = shift;
my $type = shift;
if ($type eq "file") {
unlink "$path" or return;
} else {
rmtree ($path,0,1);
}
}
sub do_sweep {
my $port=shift;
if (! -f "$port/.signature") {
print "Warning: no signature found.\n";
print "Merciless destruction will be unleashed on $port.\n";
print "Press Ctrl+C within 3 seconds to abort.\n";
sleep 3;
}
if (-f "$port/Pkgfile") {
sweep($port);
} else {
rm_emptydir($port);
}
}
sub getportdirs {
my $collection;
my @basedirs;
my $portetc = "/etc/ports/";
opendir (PORTS_DEFS,$portetc) or die "cannot open $portetc for reading";
foreach (readdir PORTS_DEFS) {
next if ($_ eq '.' or $_ eq '..');
if (/.*(rsync|httpup)$/) {
open SYNC, $portetc.$_ or die "cannot open $portetc.$_";
while (<SYNC>) {
$collection=$2 if /^(destination|ROOT_DIR)=(.+)$/;
}
close SYNC;
push (@basedirs , $collection);
} elsif (/.*git$/) {
open SYNC, $portetc.$_ or die "cannot open $portetc.$_";
while (<SYNC>) {
$collection="/usr/ports/$1" if /^NAME=(.+)$/;
}
close SYNC;
push (@basedirs , $collection);
} else {}
}
closedir PORTS_DEFS;
return @basedirs ;
}
# main
parse_args();
print_usage() if ((2*$argports-1)*(1-2*$options{auto}) < 0);
if ($options{auto} == 1) {
my @basedirs = getportdirs();
foreach my $collection (@basedirs) {
print "====> Sweeping port collection $collection\n";
foreach my $port (list_subdirs($collection)) {
do_sweep($port)
}
}
} else {
foreach my $port (@portdirs) {
do_sweep($port)
}
}