File Coverage

blib/lib/Fsdb/Filter/dbfilevalidate.pm
Criterion Covered Total %
statement 18 80 22.5
branch 0 30 0.0
condition 0 6 0.0
subroutine 6 17 35.2
pod 6 6 100.0
total 30 139 21.5


line stmt bran cond sub pod time code
1             #!/usr/bin/perl -w
2              
3             #
4             # dbfilevalidate.pm
5             # Copyright (C) 2007 by John Heidemann
6             # $Id: 3136ba0e1e91c68aac840a76440e41e75b0e4666 $
7             #
8             # This program is distributed under terms of the GNU general
9             # public license, version 2. See the file COPYING
10             # in $dblibdir for details.
11             #
12              
13              
14             package Fsdb::Filter::dbfilevalidate;
15              
16             =head1 NAME
17              
18             dbfilevalidate - insure the source input is a well-formed Fsdb file
19              
20             =head1 SYNOPSIS
21              
22             dbfilevalidate [-vc]
23              
24             =head1 DESCRIPTION
25              
26             Validates the input file to make sure it is a well-formed
27             fsdb file. If the file is well-formed, it outputs the whole file
28             and exits with a good exit code. For invalid files,
29             it exits with an error exit code and embedded error messages
30             in the stream as comments with "***" in them.
31              
32             Currently this program checks for rows with missing or extra columns.
33              
34             =head1 OPTIONS
35              
36             =over 4
37              
38             =item B<-v> or B<--errors-only>
39              
40             Output only broken lines, not the whole thing.
41              
42             =item B<-c> or B<--correct>
43              
44             Correct errors, if possible. Pad out rows with the empty value;
45             truncate rows with extra values.
46             If errors can be corrected the program exits with a good return code.
47              
48             =item C<-e E> or C<--empty E>
49              
50             give value E as the value for empty (null) records
51              
52             =back
53              
54             =for comment
55             begin_standard_fsdb_options
56              
57             This module also supports the standard fsdb options:
58              
59             =over 4
60              
61             =item B<-d>
62              
63             Enable debugging output.
64              
65             =item B<-i> or B<--input> InputSource
66              
67             Read from InputSource, typically a file name, or C<-> for standard input,
68             or (if in Perl) a IO::Handle, Fsdb::IO or Fsdb::BoundedQueue objects.
69              
70             =item B<-o> or B<--output> OutputDestination
71              
72             Write to OutputDestination, typically a file name, or C<-> for standard output,
73             or (if in Perl) a IO::Handle, Fsdb::IO or Fsdb::BoundedQueue objects.
74              
75             =item B<--autorun> or B<--noautorun>
76              
77             By default, programs process automatically,
78             but Fsdb::Filter objects in Perl do not run until you invoke
79             the run() method.
80             The C<--(no)autorun> option controls that behavior within Perl.
81              
82             =item B<--help>
83              
84             Show help.
85              
86             =item B<--man>
87              
88             Show full manual.
89              
90             =back
91              
92             =for comment
93             end_standard_fsdb_options
94              
95              
96             =head1 SAMPLE USAGE
97              
98             =head2 Input:
99              
100             #fsdb sid cid
101             1 10
102             2
103             1 12
104             2 12
105              
106             =head2 Command:
107              
108             cat TEST/dbfilevalidate_ex.in | dbvalidate
109              
110             =head2 Output:
111              
112             #fsdb sid cid
113             1 10
114             2
115             # *** line above is missing field cid.
116             1 12
117             2 12
118             # | dbfilevalidate
119              
120              
121             =head1 SEE ALSO
122              
123             L.
124              
125              
126             =head1 CLASS FUNCTIONS
127              
128             =cut
129              
130             @ISA = qw(Fsdb::Filter);
131             $VERSION = 2.0;
132              
133 1     1   3972 use strict;
  1         1  
  1         24  
134 1     1   4 use Pod::Usage;
  1         1  
  1         63  
135 1     1   4 use Carp;
  1         1  
  1         37  
136              
137 1     1   3 use Fsdb::Filter;
  1         1  
  1         14  
138 1     1   3 use Fsdb::IO::Reader;
  1         2  
  1         30  
139 1     1   4 use Fsdb::IO::Writer;
  1         1  
  1         556  
140              
141              
142             =head2 new
143              
144             $filter = new Fsdb::Filter::dbfilevalidate(@arguments);
145              
146             Create a new dbfilevalidate object, taking command-line arguments.
147              
148             =cut
149              
150             sub new ($@) {
151 0     0 1   my $class = shift @_;
152 0           my $self = $class->SUPER::new(@_);
153 0           bless $self, $class;
154 0           $self->set_defaults;
155 0           $self->parse_options(@_);
156 0           $self->SUPER::post_new();
157 0           return $self;
158             }
159              
160              
161             =head2 set_defaults
162              
163             $filter->set_defaults();
164              
165             Internal: set up defaults.
166              
167             =cut
168              
169             sub set_defaults ($) {
170 0     0 1   my($self) = @_;
171 0           $self->SUPER::set_defaults();
172 0           $self->{_correct} = undef;
173 0           $self->{_errors_only} = undef;
174             }
175              
176             =head2 parse_options
177              
178             $filter->parse_options(@ARGV);
179              
180             Internal: parse command-line arguments.
181              
182             =cut
183              
184             sub parse_options ($@) {
185 0     0 1   my $self = shift @_;
186              
187 0           my(@argv) = @_;
188             $self->get_options(
189             \@argv,
190 0     0     'help|?' => sub { pod2usage(1); },
191 0     0     'man' => sub { pod2usage(-verbose => 2); },
192             'autorun!' => \$self->{_autorun},
193             'c|correct!' => \$self->{_correct},
194             'd|debug+' => \$self->{_debug},
195             'e|empty=s' => \$self->{_empty},
196 0     0     'i|input=s' => sub { $self->parse_io_option('input', @_); },
197             'log!' => \$self->{_logprog},
198 0     0     'o|output=s' => sub { $self->parse_io_option('output', @_); },
199             'v|errors-only!' => \$self->{_errors_only},
200 0 0         ) or pod2usage(2);
201 0 0         pod2usage(2) if ($#argv != -1);
202             }
203              
204             =head2 setup
205              
206             $filter->setup();
207              
208             Internal: setup, parse headers.
209              
210             =cut
211              
212             sub setup ($) {
213 0     0 1   my($self) = @_;
214              
215 0           $self->{_lineno} = 0;
216             $self->finish_io_option('input', -comment_handler => sub {
217 0     0     $self->{_lineno}++;
218 0           $self->{_out}->write_raw(@_);
219 0           });
220 0           $self->finish_io_option('output', -clone => $self->{_in});
221             }
222              
223             =head2 run
224              
225             $filter->run();
226              
227             Internal: run over each rows.
228              
229             =cut
230             sub run ($) {
231 0     0 1   my($self) = @_;
232              
233 0           $self->{_ok} = 1;
234 0           my $read_fastpath_sub = $self->{_in}->fastpath_sub();
235 0           my $write_fastpath_sub = $self->{_out}->fastpath_sub();
236 0           my $fref;
237 0           my @columns = @{$self->{_in}->cols};
  0            
238 0           my $invert_output = $self->{_errors_only};
239 0 0         my $corrected = ($self->{_correct} ? " (corrected)" : "");
240 0           while ($fref = &{$read_fastpath_sub}()) {
  0            
241 0           my $bad_fields = '';
242 0           my $bad_field_count = 0;
243 0           my $extra_field_count = 0;
244 0           my $i;
245 0           foreach (0..$#columns) {
246 0 0         if (!defined($fref->[$_])) {
247 0 0         $bad_fields .= ($bad_fields eq '' ? "" : ", ") . $columns[$_];
248 0           $bad_field_count++;
249 0 0         $fref->[$_] = $self->{_empty} if ($self->{_correct});
250             };
251             };
252 0 0         if ($#$fref > $#columns) {
253 0           $self->{_ok} = 0;
254 0           $extra_field_count = ($#$fref - $#columns);
255 0 0         if ($self->{_correct}) {
256 0           while ($#$fref > $#columns) {
257 0           pop @$fref;
258             };
259             };
260             } ;
261 0 0 0       &{$write_fastpath_sub}($fref)
  0            
262             if ($bad_field_count || !$invert_output);
263 0 0         if ($bad_field_count > 0) {
264 0           $self->{_ok} = 0;
265 0 0         $self->{_out}->write_comment("*** line above is missing field"
266             . (($bad_field_count > 1) ? "s " : " ")
267             . $bad_fields . $corrected . ".");
268             };
269 0 0         if ($extra_field_count) {
270 0 0         $self->{_out}->write_comment("*** line above has $extra_field_count extra column" . ($extra_field_count == 1 ? "" : "s") . $corrected . ".");
271             };
272             };
273 0 0         $self->{_out}->write_comment("*** dbfilevalidate: some lines had errors$corrected.") if (!$self->{_ok});
274             }
275              
276              
277              
278             =head2 finish
279              
280             $filter->finish();
281              
282             Internal: write trailer.
283              
284             =cut
285             sub finish ($) {
286 0     0 1   my($self) = @_;
287              
288 0           $self->SUPER::finish();
289 0 0 0       exit 1 if (!$self->{_ok} && !$self->{_correct});
290             }
291              
292             =head1 AUTHOR and COPYRIGHT
293              
294             Copyright (C) 1991-2008 by John Heidemann
295              
296             This program is distributed under terms of the GNU general
297             public license, version 2. See the file COPYING
298             with the distribution for details.
299              
300             =cut
301              
302             1;