xref: /openbsd/usr.sbin/pkg_add/OpenBSD/Error.pm (revision 989d1a3d)
1# ex:ts=8 sw=4:
2# $OpenBSD: Error.pm,v 1.22 2010/01/17 11:56:46 espie Exp $
3#
4# Copyright (c) 2004-2010 Marc Espie <espie@openbsd.org>
5#
6# Permission to use, copy, modify, and distribute this software for any
7# purpose with or without fee is hereby granted, provided that the above
8# copyright notice and this permission notice appear in all copies.
9#
10# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16
17use strict;
18use warnings;
19
20package OpenBSD::Auto;
21sub cache(*&)
22{
23	my ($sym, $code) = @_;
24	my $callpkg = caller;
25	my $actual = sub {
26		my $self = shift;
27		if (!defined $self->{$sym}) {
28			$self->{$sym} = &$code($self);
29		}
30		return $self->{$sym};
31	};
32	no strict 'refs';
33	*{$callpkg."::$sym"} = $actual;
34}
35
36package OpenBSD::Handler;
37
38my $list = [];
39
40sub register
41{
42	my ($class, $code) = @_;
43	push(@$list, $code);
44}
45
46my $handler = sub {
47	my $sig = shift;
48	for my $c (@$list) {
49		&$c($sig);
50	}
51	$SIG{$sig} = 'DEFAULT';
52	kill $sig, $$;
53};
54
55sub reset
56{
57	$SIG{'INT'} = $handler;
58	$SIG{'QUIT'} = $handler;
59	$SIG{'HUP'} = $handler;
60	$SIG{'KILL'} = $handler;
61	$SIG{'TERM'} = $handler;
62}
63
64__PACKAGE__->reset;
65
66package OpenBSD::Error;
67require Exporter;
68our @ISA=qw(Exporter);
69our @EXPORT=qw(System VSystem Copy Unlink Fatal Warn Usage set_usage
70    try throw catch catchall rethrow);
71
72our ($FileName, $Line, $FullMessage);
73
74my @signal_name = ();
75
76use Carp;
77
78sub fillup_names
79{
80	{
81	# XXX force autoload
82	package verylocal;
83
84	require POSIX;
85	POSIX->import(qw(signal_h));
86	}
87
88	for my $sym (keys %POSIX::) {
89		next unless $sym =~ /^SIG([A-Z].*)/;
90		$signal_name[eval "&POSIX::$sym()"] = $1;
91	}
92	# extra BSD signals
93	$signal_name[5] = 'TRAP';
94	$signal_name[7] = 'IOT';
95	$signal_name[10] = 'BUS';
96	$signal_name[12] = 'SYS';
97	$signal_name[16] = 'URG';
98	$signal_name[23] = 'IO';
99	$signal_name[24] = 'XCPU';
100	$signal_name[25] = 'XFSZ';
101	$signal_name[26] = 'VTALRM';
102	$signal_name[27] = 'PROF';
103	$signal_name[28] = 'WINCH';
104	$signal_name[29] = 'INFO';
105}
106
107sub find_signal
108{
109	my $number =  shift;
110
111	if (@signal_name == 0) {
112		fillup_names();
113	}
114
115	return $signal_name[$number] || $number;
116}
117
118sub child_error
119{
120	my $error = $?;
121
122	my $extra = "";
123
124	if ($error & 128) {
125		$extra = " (core dumped)";
126	}
127	if ($error & 127) {
128		return "killed by signal ". find_signal($error & 127).$extra;
129	} else {
130		return "exit(". ($error >> 8) . ")$extra";
131	}
132}
133
134sub System
135{
136	my $r = system(@_);
137	if ($r != 0) {
138		print "system(", join(", ", @_), ") failed: ", child_error(),
139		    "\n";
140	}
141	return $r;
142}
143
144sub VSystem
145{
146	my $verbose = shift;
147	if (!$verbose) {
148		&System;
149	} else {
150		print "Running ", join(' ', @_);
151		my $r = system(@_);
152		if ($r != 0) {
153			print "... failed: ", child_error(), "\n";
154		} else {
155			print "\n";
156		}
157	}
158}
159
160sub Copy
161{
162	require File::Copy;
163
164	my $r = File::Copy::copy(@_);
165	if (!$r) {
166		print "copy(", join(',', @_),") failed: $!\n";
167	}
168	return $r;
169}
170
171sub Unlink
172{
173	my $verbose = shift;
174	my $r = unlink @_;
175	if ($r != @_) {
176		print "rm @_ failed: removed only $r targets, $!\n";
177	} elsif ($verbose) {
178		print "rm @_\n";
179	}
180	return $r;
181}
182
183sub Fatal
184{
185	croak @_;
186}
187
188sub Warn
189{
190	print STDERR @_;
191}
192
193sub new
194{
195	my $class = shift;
196	bless {messages=>{}, dirs_okay=>{}}, $class;
197}
198
199sub set_pkgname
200{
201	my ($self, $pkgname) = @_;
202	$self->{pkgname} = $pkgname;
203	if (!defined $self->{messages}->{$pkgname}) {
204		$self->{messages}->{$pkgname} = [];
205	}
206	$self->{output} = $self->{messages}->{$pkgname};
207}
208
209sub warn
210{
211	&OpenBSD::Error::print;
212}
213
214sub fatal
215{
216	my $self = shift;
217	if (defined $self->{pkgname}) {
218		unshift @_, $self->{pkgname}, ':';
219	}
220	croak @_;
221}
222
223sub print
224{
225	my $self = shift;
226	push(@{$self->{output}}, join('', @_));
227}
228
229sub delayed_output
230{
231	my $self = shift;
232	for my $pkg (sort keys %{$self->{messages}}) {
233		my $msgs = $self->{messages}->{$pkg};
234		if (@$msgs > 0) {
235			print "--- $pkg -------------------\n";
236			print @$msgs;
237		}
238	}
239	$self->{messages} = {};
240}
241
242sub system
243{
244	my $self = shift;
245	if (open(my $grab, "-|", @_)) {
246		my $_;
247		while (<$grab>) {
248			$self->print($_);
249		}
250		if (!close $grab) {
251		    $self->print("system(", join(", ", @_), ") failed: $! ",
252		    	child_error(), "\n");
253		}
254		return $?;
255	} else {
256		    $self->print("system(", join(", ", @_),
257		    	") was not run: $!", child_error(), "\n");
258	}
259}
260
261my @usage_line;
262
263sub set_usage
264{
265	@usage_line = @_;
266}
267
268sub Usage
269{
270	my $code = 0;
271	if (@_) {
272		print STDERR "$0: ", @_, "\n";
273		$code = 1;
274	}
275	print STDERR "Usage: ", shift(@usage_line), "\n";
276	for my $l (@usage_line) {
277		print STDERR "       $l\n";
278	}
279	exit($code);
280}
281
282sub dienow
283{
284	my ($error, $handler) = @_;
285	if ($error) {
286		if ($error =~ m/^(.*?)(?:\s+at\s+(.*)\s+line\s+(\d+)\.?)?$/o) {
287			local $_ = $1;
288			$FileName = $2;
289			$Line = $3;
290			$FullMessage = $error;
291
292			$handler->exec($error, '', $1, $2, $3);
293		} else {
294			die "Fatal error: can't parse $error";
295		}
296	}
297}
298
299sub try(&@)
300{
301	my ($try, $catch) = @_;
302	eval { &$try };
303	dienow($@, $catch);
304}
305
306sub throw
307{
308	croak @_;
309
310}
311
312sub rethrow
313{
314	my $e = shift;
315	die $e if $e;
316}
317
318sub catch(&)
319{
320		bless $_[0], "OpenBSD::Error::catch";
321}
322
323sub catchall(&)
324{
325	bless $_[0], "OpenBSD::Error::catchall";
326}
327
328package OpenBSD::Error::catch;
329sub exec
330{
331	my ($self, $full, $e) = @_;
332	if ($e) {
333		&$self;
334	} else {
335		die $full;
336	}
337}
338
339package OpenBSD::Error::catchall;
340sub exec
341{
342	my ($self, $full, $e) = @_;
343	&$self;
344}
345
3461;
347