File Coverage

blib/lib/IPC/Filter.pm
Criterion Covered Total %
statement 83 92 90.2
branch 25 34 73.5
condition 5 6 83.3
subroutine 12 12 100.0
pod 1 1 100.0
total 126 145 86.9


line stmt bran cond sub pod time code
1             =head1 NAME
2              
3             IPC::Filter - filter data through an external process
4              
5             =head1 SYNOPSIS
6              
7             use IPC::Filter qw(filter);
8              
9             $compressed_data = filter($data, "bzip2");
10              
11             =head1 DESCRIPTION
12              
13             The C function provided by this module passes data through an
14             external command, thus providing filtering in non-pipeline situations.
15              
16             =cut
17              
18             package IPC::Filter;
19              
20 1     1   187703 { use 5.006; }
  1         5  
  1         391  
21 1     1   172 use warnings;
  1         3  
  1         707  
22 1     1   180 use strict;
  1         169  
  1         1384  
23              
24 1     1   1614 use Errno 1.00 qw(EPIPE);
  1         1566  
  1         141  
25 1     1   1007 use IPC::Open3 1.01 qw(open3);
  1         2802  
  1         67  
26 1     1   825 use IPC::Signal 1.00 qw(sig_name);
  1         810  
  1         76  
27 1     1   7 use IO::Handle 1.12;
  1         25  
  1         56  
28 1     1   948 use IO::Poll 0.01 qw(POLLIN POLLOUT POLLERR POLLHUP);
  1         1007  
  1         87  
29 1     1   972 use POSIX qw(_exit);
  1         8222  
  1         7  
30 1     1   1348 use Symbol qw(gensym);
  1         2  
  1         67  
31              
32             our $VERSION = "0.004";
33              
34 1     1   927 use parent "Exporter";
  1         300  
  1         6  
35             our @EXPORT_OK = qw(filter);
36              
37             =head1 FUNCTIONS
38              
39             =over
40              
41             =item filter(DATA, SHELL_COMMAND)
42              
43             =item filter(DATA, PROGRAM, ARGS ...)
44              
45             The SHELL_COMMAND, or the PROGRAM with ARGS if more arguments are
46             supplied, is executed as a separate process. (The arguments other
47             than DATA are ultimately passed to C; see L
48             for explanation of the choice between the two forms.) The DATA (which
49             must be either a simple string or a reference to a string) is supplied
50             to the process on its standard input, and the process's standard output
51             is captured and returned (as a simple string).
52              
53             If the process exits with a non-zero exit code or on a signal, the
54             function will C. In the case of a non-zero exit code, the C
55             message will duplicate the process's standard error output; in any other
56             case, the error output is discarded.
57              
58             =cut
59              
60             my $chunksize = 4096;
61              
62             sub filter($@) {
63 9     9 1 13721 my $data = \shift(@_);
64 9 100 100     148 if(@_ == 0 || $_[0] eq "-") {
65 2         14 die "filter: invalid command\n";
66             }
67 7 50       52 if(ref($data) eq "REF") {
68 0         0 $data = $$data;
69             }
70 7         81 my $stdin = gensym;
71 7         457 my $stdout = gensym;
72 7         114 my $stderr = gensym;
73             # Note: perl bug (bug in IPC::Open3 version 1.0106, bug ID
74             # #32198): if the exec fails in the subprocess created by open3(),
75             # it uses die() to emit its error message and terminate. If an
76             # exception handler is installed using eval {}, execution in the
77             # subprocess continues there instead of the process terminating.
78             # We avoid nastiness by catching the exception ourselves and
79             # doing the right thing.
80 7         173 my $parent_pid = $$;
81 7         13 my $child_pid = eval { local $SIG{__DIE__};
  7         36  
82 7         69 open3($stdin, $stdout, $stderr, @_);
83             };
84 7 50       54809 if($@ ne "") {
85 0         0 my $err = $@;
86 0 0       0 die $err if $$ == $parent_pid;
87 0         0 print STDERR $err;
88 0         0 _exit 255;
89             }
90 7         274 local $SIG{PIPE} = "IGNORE";
91 7         241 my $poll = IO::Poll->new;
92 7         156 my $datalen = length($$data);
93 7 50       43 if($datalen == 0) {
94 0         0 $stdin->close;
95             } else {
96 7         91 $poll->mask($stdin => POLLOUT | POLLERR | POLLHUP);
97             }
98 7         314 $poll->mask($stdout => POLLIN | POLLERR | POLLHUP);
99 7         253 $poll->mask($stderr => POLLIN | POLLERR | POLLHUP);
100 7         263 my $datapos = 0;
101 7         41 my @out;
102             my @err;
103 7         68 while($poll->handles) {
104 20         350 $poll->poll;
105 20 100 66     8672 if($datapos != $datalen && $poll->events($stdin)) {
106 7         368 my $n = $stdin->syswrite($$data, $chunksize, $datapos);
107 7 100       307 if(defined $n) {
    50          
108 6         458 $datapos += $n;
109             } elsif($! == EPIPE) {
110 1         7 $datapos = $datalen;
111             } else {
112 0         0 die "filter: stdin: $!\n";
113             }
114 7 50       29 if($datapos == $datalen) {
115 7         41 $poll->remove($stdin);
116 7         476 $stdin->close;
117             }
118             }
119 20 100       382 if($poll->events($stdout)) {
120 11         193 my $output;
121 11 50       68 unless(defined $stdout->sysread($output, $chunksize)) {
122 0         0 die "filter: stdout: $!\n";
123             }
124 11 100       6546 if($output eq "") {
125 7         24 $poll->remove($stdout);
126             } else {
127 4         30 push @out, $output;
128             }
129             }
130 20 100       290 if($poll->events($stderr)) {
131 9         123 my $output;
132 9 50       36 unless(defined $stderr->sysread($output, $chunksize)) {
133 0         0 die "filter: stderr: $!\n";
134             }
135 9 100       164 if($output eq "") {
136 7         27 $poll->remove($stderr);
137             } else {
138 2         25 push @err, $output;
139             }
140             }
141             }
142 7         1480 waitpid $child_pid, 0;
143 7         76 my $status = $?;
144 7 100       22 if($status == 0) {
145 4         300 return join("", @out);
146             }
147 3 100       31 if($status & 127) {
148 1         27 die "filter: process died on SIG".sig_name($status & 127)."\n";
149             } else {
150 2         158 die join("", "filter: process exited with status ",
151             $status >> 8, "\n", @err);
152             }
153             }
154              
155             =back
156              
157             =head1 SEE ALSO
158              
159             L
160              
161             =head1 AUTHOR
162              
163             Andrew Main (Zefram)
164              
165             =head1 COPYRIGHT
166              
167             Copyright (C) 2004, 2007, 2010, 2011
168             Andrew Main (Zefram)
169              
170             =head1 LICENSE
171              
172             This module is free software; you can redistribute it and/or modify it
173             under the same terms as Perl itself.
174              
175             =cut
176              
177             1;