xref: /openbsd/usr.sbin/pkg_add/pkg_mklocatedb (revision 91f110e0)
1#! /usr/bin/perl
2# Copyright (c) 2005-2010 Marc Espie <espie@openbsd.org>
3# $OpenBSD: pkg_mklocatedb,v 1.41 2014/03/18 18:53:29 espie Exp $
4#
5# Permission to use, copy, modify, and distribute this software for any
6# purpose with or without fee is hereby granted, provided that the above
7# copyright notice and this permission notice appear in all copies.
8#
9# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16
17use strict;
18use warnings;
19
20use OpenBSD::PackageInfo;
21use OpenBSD::PackingList;
22use OpenBSD::Getopt;
23use OpenBSD::Error;
24use OpenBSD::Paths;
25use OpenBSD::AddCreateDelete;
26
27package OpenBSD::Pkgmklocatedb::State;
28our @ISA = qw(OpenBSD::AddCreateDelete::State);
29
30sub handle_options
31{
32	my $state = shift;
33	$state->{no_exports} = 1;
34	$state->SUPER::handle_options('aCd:Knqs:x:r:p:Pu',
35	    '[-aCKnPqu] [-d repository] [-p portsdir] [-r release] [-s src] ',
36	    '[-x X11src] [pkg-name [...]]');
37	$state->{srcdir} = $state->opt('s');
38	$state->{xdir} = $state->opt('x');
39	$state->{releasedir} = $state->opt('r');
40	$state->{portsdir} = $state->opt('p');
41	$state->{pkgdir} = $state->opt('d');
42	$state->{quiet} = $state->opt('q');
43	$state->{pkgpath} = $state->opt('P');
44	$state->{allinfo} = $state->opt('a');
45	$state->{nopipe} = $state->opt('n');
46	$state->{check} = $state->opt('C');
47	$state->{full} = $state->opt('K');
48	$state->{update} = $state->opt('u');
49	if ($state->{check}) {
50		unless ($state->{srcdir} or $state->{portsdir}) {
51			$state->usage("-C without -s dir or -x dir");
52		}
53	}
54}
55
56
57package OpenBSD::PackingElement;
58sub print_name {}
59sub set_header {}
60
61package OpenBSD::PackingElement::Name;
62sub set_header
63{
64	my ($self, $state) = @_;
65	$state->{currentheader} = $self->{name}.':';
66}
67
68package OpenBSD::PackingElement::ExtraInfo;
69sub set_header
70{
71	my ($self, $state) = @_;
72	if ($state->{allinfo}) {
73		$state->{currentheader} .=  $self->{subdir}.':';
74	} elsif ($state->{pkgpath}) {
75		$state->{currentheader} = $self->{subdir}.':';
76	}
77	$state->{done}{$self->{subdir}} = 1;
78	$state->errsay($state->{currentheader}) unless $state->{quiet};
79}
80
81package OpenBSD::PackingElement::FileObject;
82sub object_name
83{
84	my ($self, $state) = @_;
85	if ($state->{full}) {
86		if ($self->needs_keyword) {
87			return "\@".$self->keyword." ".$self->fullname;
88		}
89	}
90	return $self->fullname;
91}
92
93sub print_name
94{
95	my ($self, $state) = @_;
96	print {$state->{out}} $state->{currentheader},
97	    $self->object_name($state), "\n";
98}
99
100package OpenBSD::PackingElement::Action;
101sub print_name
102{
103	my ($self, $state) = @_;
104	print {$state->{out}} $state->{currentheader}, $self->fullstring, "\n";
105}
106
107package OpenBSD::PackingElement::ExeclikeAction;
108sub print_name
109{
110	my ($self, $state) = @_;
111	print {$state->{out}} $state->{currentheader}, "\@".
112	    $self->keyword, " ", $self->{expanded}, "\n";
113}
114
115package OpenBSD::PackingElement::DirBase;
116sub print_name
117{
118	my ($self, $state) = @_;
119	print {$state->{out}} $state->{currentheader},
120	    $self->object_name($state), "/\n";
121}
122
123package main;
124sub tag
125{
126	my ($state, $dir, $set, $rev) = @_;
127	my $r;
128	if ($state->{allinfo}) {
129		$r = "$dir/$set:$set$rev";
130	} elsif ($state->{pkgpath}) {
131		$r = "$dir/$set";
132	} else {
133		$r = "$set$rev";
134	}
135	$state->errsay($r) unless $state->{quiet};
136	return $r;
137}
138
139my ($rev, $arch);
140sub findos
141{
142	my $cmd = OpenBSD::Paths->uname." -mr";
143	($rev, $arch) = split(/\s+/o, `$cmd`);
144	chomp $arch;
145	$rev =~ s/\.//;
146}
147
148sub open_output
149{
150	my $state = shift;
151
152
153	if ($state->{nopipe} or -t STDOUT) {
154		$state->{out} = \*STDOUT;
155	} else {
156		my $MKLOCATEDB = OpenBSD::Paths->mklocatedb;
157
158		open $state->{out}, "|-", $MKLOCATEDB, $MKLOCATEDB or
159		    $state->fatal("couldn't open pipe to mklocatedb: #1", $!);
160	}
161}
162
163sub sync_error
164{
165	my ($state, $e, $set) = @_;
166	if ($set =~ m/etc/) {
167		$state->errsay("Couldn't find #1 from set #2:\n\tDid you run sysmerge recently ?", $e, $set);
168	} else {
169		$state->errsay("Couldn't find #1 from set #2:\n\tsrc and installation out of synch ?", $e, $set);
170		$state->{fatals}++;
171	}
172}
173
174sub do_source
175{
176	my ($state, $src, $tag, @sets) = @_;
177	findos() if !defined $arch;
178	my $dir = "$src/distrib/sets/lists";
179	for my $set (@sets) {
180		my $tag = tag($state, $tag, $set, $rev);
181		my $output = 0;
182		for my $f ("$dir/$set/mi", "$dir/$set/md.$arch") {
183			open my $l, '<', $f or next;
184			while (my $e = <$l>) {
185				chomp $e;
186				$e =~ s/^\.//;
187				if ($state->{check} && !-e $e) {
188					sync_error($state, $e, $set);
189				}
190				print {$state->{out}} "$tag:$e\n";
191				$output = 1;
192			}
193		}
194		if (!$output) {
195			$state->fatal("Couldn't find set #1", $set);
196		}
197	}
198	if ($state->{fatals}) {
199		$state->fatal("Files not found, can't continue");
200    	}
201}
202
203sub do_release
204{
205	my $state = shift;
206
207	require OpenBSD::Ustar;
208
209	opendir(my $dir, $state->{releasedir}) or return;
210	while (my $e = readdir $dir) {
211		if ($e =~ m/^(\w+\d\d)\.tgz$/o) {
212			my $set = $1;
213			open my $arc, '-|', OpenBSD::Paths->gzip, '-c', '-d',
214			    "--", $state->{releasedir}."/".$e or
215			$state->fatal("couldn't open pipe from gzip: #1", $!);
216
217			my $u = OpenBSD::Ustar->new($arc, $state, '/');
218			while (my $f = $u->next) {
219				my $name = $f->{name};
220				$name =~ s/^\.//o;
221				print {$state->{out}} "$set:$name\n";
222			}
223			close $arc;
224		}
225	}
226	closedir($dir);
227}
228
229sub print_out
230{
231	my ($plist, $state) = @_;
232
233	$plist->set_header($state);
234	$plist->print_name($state);
235}
236
237sub do_portsdir
238{
239	my $state = shift;
240	my $make = $ENV{MAKE} || 'make';
241	my $target = defined $ENV{SUBDIRLIST} ?
242	    'print-plist' : 'print-plist-all';
243	delete $ENV{FLAVOR};
244	delete $ENV{SUBPACKAGE};
245	open my $in, "cd $state->{portsdir} && $make $target |";
246	my $done = 0;
247	while (!$done) {
248		my $plist = OpenBSD::PackingList->read($in,
249		    sub {
250			my ($fh, $cont) = @_;
251			while (<$fh>) {
252				return if m/^\=\=\=\> /o;
253				&$cont($_);
254			}
255			$done = 1;
256		    });
257		if (defined $plist && defined $plist->pkgname) {
258			print_out($plist, $state);
259		}
260	}
261	close($in);
262}
263
264sub do_pkgdir
265{
266	my $state = shift;
267
268	require File::Find;
269	no warnings qw(once);
270	$state->fatal("Bad argument: #1 is not a directory", $state->{pkgdir})
271	    unless -d $state->{pkgdir};
272	File::Find::find(
273	    sub {
274		return unless -f $_;
275		my $plist = $state->repo->grabPlist($File::Find::name);
276		return unless defined $plist;
277		print_out($plist, $state);
278	    }, $state->{pkgdir});
279}
280
281sub copy_stdin
282{
283	my $state = shift;
284	while (<STDIN>) {
285		# if we find something that looks like a pkgpath we've done
286		# assume we were updating it
287		if (m,([^:]*/[^:]*)\:,) {
288			next if defined $state->{done}{$1};
289		}
290		print {$state->{out}} $_;
291	}
292}
293
294my $state = OpenBSD::Pkgmklocatedb::State->new("pkg_mklocatedb");
295$state->handle_options;
296
297open_output($state);
298
299if ($state->{srcdir}) {
300	do_source($state, $state->{srcdir}, 'src',
301	    qw(base comp etc game));
302}
303if ($state->{xdir}) {
304	do_source($state, $state->{xdir}, 'xenocara',
305	    qw(xbase xetc xfont xserv xshare));
306}
307if ($state->{releasedir}) {
308	do_release($state);
309}
310
311if ($state->{portsdir}) {
312	do_portsdir($state);
313} elsif ($state->{pkgdir}) {
314	do_pkgdir($state);
315} elsif (@ARGV == 0) {
316	if (!$state->{update}) {
317		$state->progress->for_list("Scanning installation",
318		    [installed_packages()], sub {
319			my $pkgname = shift;
320			my $plist =
321			    OpenBSD::PackingList->from_installation($pkgname);
322			return unless defined $plist;
323			print_out($plist, $state);
324		    });
325	}
326} else {
327	$state->progress->for_list("Scanning packages", \@ARGV,
328	    sub {
329	    	my $pkgname = shift;
330		my $plist = $state->repo->grabPlist($pkgname);
331		next unless $plist;
332		print_out($plist, $state);
333	    });
334}
335if ($state->{update}) {
336	copy_stdin($state);
337}
338