File Coverage

blib/lib/Process/Status.pm
Criterion Covered Total %
statement 52 56 92.8
branch 19 24 79.1
condition 1 3 33.3
subroutine 22 25 88.0
pod 9 10 90.0
total 103 118 87.2


line stmt bran cond sub pod time code
1 1     1   70658 use strict;
  1         12  
  1         38  
2 1     1   7 use warnings;
  1         2  
  1         47  
3             package Process::Status;
4             # ABSTRACT: a handle on process termination, like $?
5             $Process::Status::VERSION = '0.009';
6 1     1   5 use Config ();
  1         9  
  1         750  
7              
8             #pod =head1 OVERVIEW
9             #pod
10             #pod When you run a system command with C or C or a number of other
11             #pod mechanisms, the process termination status gets put into C<$?> as an integer.
12             #pod In C, it's just an integer, and it stores a few pieces of data in different
13             #pod bits.
14             #pod
15             #pod Process::Status just provides a few simple methods to make it easier to
16             #pod inspect. It exists almost entirely to provide C and C,
17             #pod which provide a simple decomposition of C<$?>.
18             #pod
19             #pod Methods called on C without first calling a constructor will
20             #pod work on an implicitly-constructed object using the current value of C<$?>. To
21             #pod get an object for a specific value, you can call C and pass an integer.
22             #pod You can also call C with no arguments to get an object for the current
23             #pod value of C<$?>, if you want to keep that ugly variable out of your code.
24             #pod
25             #pod =method new
26             #pod
27             #pod my $ps = Process::Status->new( $status );
28             #pod my $ps = Process::Status->new; # acts as if you'd passed $?
29             #pod
30             #pod =cut
31              
32 19 100   19   85 sub _self { ref $_[0] ? $_[0] : $_[0]->new($?); }
33              
34             sub new {
35 4 50   4 1 6679 my $status = defined $_[1] ? $_[1] : $?;
36 4 100       16 return bless \$status, $_[0] if $status >= 0;
37              
38 2         14 return bless [ $status, "$!", 0+$! ], 'Process::Status::Negative';
39             }
40              
41             #pod =method status_code
42             #pod
43             #pod This returns the value of the integer return value, as you might have found in
44             #pod C<$?>.
45             #pod
46             #pod =cut
47              
48             sub status_code {
49 2     2 1 5 ${ $_[0]->_self }
  2         5  
50             }
51              
52             sub pid_t {
53             # historical nonsense
54 0     0 0 0 ${ $_[0]->_self }
  0         0  
55             }
56              
57             #pod =method is_success
58             #pod
59             #pod This method returns true if the status code is zero.
60             #pod
61             #pod =cut
62              
63 2     2 1 3 sub is_success { ${ $_[0]->_self } == 0 }
  2         5  
64              
65             #pod =method exitstatus
66             #pod
67             #pod This method returns the exit status encoded in the status. In other words,
68             #pod it's the number in the top eight bits.
69             #pod
70             #pod =cut
71              
72 2     2 1 11 sub exitstatus { ${ $_[0]->_self } >> 8 }
  2         6  
73              
74             #pod =method signal
75             #pod
76             #pod This returns the signal caught by the process, or zero.
77             #pod
78             #pod =cut
79              
80 2     2 1 4 sub signal { ${ $_[0]->_self } & 127 }
  2         6  
81              
82             #pod =method cored
83             #pod
84             #pod This method returns true if the process dumped core.
85             #pod
86             #pod =cut
87              
88 2     2 1 5 sub cored { !! (${ $_[0]->_self } & 128) }
  2         5  
89              
90             #pod =method as_struct
91             #pod
92             #pod This method returns a hashref describing the status. Its exact contents may
93             #pod change over time; it is meant for human, not computer, consumption.
94             #pod
95             #pod =cut
96              
97             sub as_struct {
98 2     2 1 6 my $self = $_[0]->_self;
99              
100 2         7 my $rc = $self->status_code;
101              
102             return {
103 2 100       25 status_code => $rc,
    100          
    50          
104             ($rc == -1 ? () : (
105             exitstatus => $rc >> 8,
106             cored => ($rc & 128) ? 1 : 0,
107              
108             (($rc & 127) ? (signal => $rc & 127) : ())
109             )),
110             };
111             }
112              
113             my %SIGNAME;
114             sub __signal_name {
115 2     2   4 my ($signal) = @_;
116 2 100       6 unless (%SIGNAME) {
117 1         116 my @names = split /\x20/, $Config::Config{sig_name};
118 1         75 $SIGNAME{$_} = "SIG$names[$_]" for (1 .. $#names);
119             }
120              
121 2   33     9 return($SIGNAME{ $signal } || "signal $signal");
122             }
123              
124             #pod =method as_string
125             #pod
126             #pod This method returns a string describing the status. Its exact contents may
127             #pod change over time; it is meant for human, not computer, consumption.
128             #pod
129             #pod Roughly, you might get things like this:
130             #pod
131             #pod exited 0
132             #pod exited 92
133             #pod exited 2, caught SIGDERP
134             #pod exited 2, caught SIGSEGV; dumped core
135             #pod
136             #pod =cut
137              
138             sub as_string {
139 4     4 1 23 my $self = $_[0]->_self;
140 4         8 my $rc = $$self;
141 4         12 my $str = "exited " . ($rc >> 8);
142 4 100       27 $str .= ", caught " . __signal_name($rc & 127) if $rc & 127;
143 4 100       17 $str .= "; dumped core" if $rc & 128;
144              
145 4         278 return $str;
146             }
147              
148             #pod =method assert_ok
149             #pod
150             #pod Process::Status->assert_ok($program_name);
151             #pod
152             #pod This method does nothing if C<$?> is 0. Otherwise, it croaks with a message
153             #pod like:
154             #pod
155             #pod your-program-name exited 13, caught SIGNES
156             #pod
157             #pod If a program name is not provided, "program" is used.
158             #pod
159             #pod =cut
160              
161             sub assert_ok {
162 3     3 1 674 my $self = $_[0]->_self;
163 3 50       10 return if $self->is_success;
164 3 50       9 my $name = @_ > 1 ? $_[1] : "program";
165              
166 3         17 require Carp;
167 3         16 Carp::croak("$name " . $self->as_string);
168             }
169              
170             {
171             package Process::Status::Negative;
172             $Process::Status::Negative::VERSION = '0.009';
173 1     1   303 BEGIN { our @ISA = 'Process::Status' }
174 0     0   0 sub status_code { $_[0][0] }
175 0     0   0 sub pid_t { $_[0][0] } # historical nonsense
176 1     1   4 sub is_success { return }
177 1     1   12 sub exitstatus { $_[0][0] }
178 1     1   5 sub signal { 0 }
179 1     1   9 sub cored { return }
180              
181             sub as_struct {
182             return {
183 1     1   9 status_code => $_[0][0],
184             strerror => $_[0][1],
185             errno => $_[0][2],
186             }
187             }
188              
189             sub as_string {
190 3     3   218 qq{did not run; \$? was $_[0][0], \$! was "$_[0][1]" (errno $_[0][2])}
191             }
192              
193             sub assert_ok {
194 1     1   7 require Carp;
195 1 50       5 my $name = @_ > 1 ? $_[1] : "program";
196 1         5 Carp::croak("$name " . $_[0]->as_string);
197             }
198             }
199              
200             1;
201              
202             __END__