xref: /openbsd/usr.sbin/pkg_add/OpenBSD/Error.pm (revision becd6ee6)
1# ex:ts=8 sw=4:
2# $OpenBSD: Error.pm,v 1.21 2010/01/09 14:49:53 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
76sub Carp::croak;
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	require Carp;
186	Carp::croak "Expected: @_";
187}
188
189sub Warn
190{
191	print STDERR @_;
192}
193
194sub new
195{
196	my $class = shift;
197	bless {messages=>{}, dirs_okay=>{}}, $class;
198}
199
200sub set_pkgname
201{
202	my ($self, $pkgname) = @_;
203	$self->{pkgname} = $pkgname;
204	if (!defined $self->{messages}->{$pkgname}) {
205		$self->{messages}->{$pkgname} = [];
206	}
207	$self->{output} = $self->{messages}->{$pkgname};
208}
209
210sub warn
211{
212	&OpenBSD::Error::print;
213}
214
215sub fatal
216{
217	my $self = shift;
218	require Carp;
219	if (defined $self->{pkgname}) {
220		Carp::croak("Expected: ", $self->{pkgname}, ':', @_);
221	} else {
222		Carp::croak("Expected: ", @_);
223	}
224}
225
226sub print
227{
228	my $self = shift;
229	push(@{$self->{output}}, join('', @_));
230}
231
232sub delayed_output
233{
234	my $self = shift;
235	for my $pkg (sort keys %{$self->{messages}}) {
236		my $msgs = $self->{messages}->{$pkg};
237		if (@$msgs > 0) {
238			print "--- $pkg -------------------\n";
239			print @$msgs;
240		}
241	}
242	$self->{messages} = {};
243}
244
245sub system
246{
247	my $self = shift;
248	if (open(my $grab, "-|", @_)) {
249		my $_;
250		while (<$grab>) {
251			$self->print($_);
252		}
253		if (!close $grab) {
254		    $self->print("system(", join(", ", @_), ") failed: $! ",
255		    	child_error(), "\n");
256		}
257		return $?;
258	} else {
259		    $self->print("system(", join(", ", @_),
260		    	") was not run: $!", child_error(), "\n");
261	}
262}
263
264my @usage_line;
265
266sub set_usage
267{
268	@usage_line = @_;
269}
270
271sub Usage
272{
273	my $code = 0;
274	if (@_) {
275		print STDERR "$0: ", @_, "\n";
276		$code = 1;
277	}
278	print STDERR "Usage: ", shift(@usage_line), "\n";
279	for my $l (@usage_line) {
280		print STDERR "       $l\n";
281	}
282	exit($code);
283}
284
285sub dienow
286{
287	my ($error, $handler) = @_;
288	if ($error) {
289		if ($error =~ m/^(Expected\:\s+)?(.*?)(?:\s+at\s+(.*)\s+line\s+(\d+)\.?)?$/o) {
290			local $_ = $2;
291			$FileName = $3;
292			$Line = $4;
293			$FullMessage = $error;
294
295			$handler->exec($error, $1, $2, $3, $4);
296		} else {
297			die "Fatal error: can't parse $error";
298		}
299	}
300}
301
302sub try(&@)
303{
304	my ($try, $catch) = @_;
305	eval { &$try };
306	dienow($@, $catch);
307}
308
309sub throw
310{
311	require Carp;
312	Carp::croak "Expected: @_";
313
314}
315
316sub rethrow
317{
318	my $e = shift;
319	die $e if $e;
320}
321
322sub catch(&)
323{
324		bless $_[0], "OpenBSD::Error::catch";
325}
326
327sub catchall(&)
328{
329	bless $_[0], "OpenBSD::Error::catchall";
330}
331
332package OpenBSD::Error::catch;
333sub exec
334{
335	my ($self, $full, $e) = @_;
336	if ($e) {
337		&$self;
338	} else {
339		die $full;
340	}
341}
342
343package OpenBSD::Error::catchall;
344sub exec
345{
346	my ($self, $full, $e) = @_;
347	&$self;
348}
349
3501;
351