File Coverage

blib/lib/App/Framework/Extension/Filter.pm
Criterion Covered Total %
statement 167 175 95.4
branch 34 44 77.2
condition 11 20 55.0
subroutine 21 22 95.4
pod 4 4 100.0
total 237 265 89.4


line stmt bran cond sub pod time code
1             package App::Framework::Extension::Filter ;
2              
3             =head1 NAME
4              
5             App::Framework::Extension::Filter - Script filter application object
6              
7             =head1 SYNOPSIS
8              
9             use App::Framework '::Filter' ;
10              
11              
12             =head1 DESCRIPTION
13              
14             Application that filters a file or files to produce some other output
15              
16              
17             =head2 Application Subroutines
18              
19             This extension modifies the normal call flow for the application subroutines. The extension calls the subroutines
20             for each input file being filtered. Also, the main 'app' subroutine is called for each of the lines of text in the input file.
21              
22             The pseudo-code for the extension is:
23              
24             FOREACH input file
25            
26             call 'app_start' subroutine
27             FOREACH input line
28             call 'app' subroutine
29             END
30             call 'app_end' subroutine
31             END
32              
33             For each input file, a state HASH is created and passed as a reference to the application subroutines. The state HASH contains
34             various values maintained by the extension, but the application may add it's own additional values to the HASH. These values will
35             be passed unmodified to each of the application subroutine calls.
36              
37             The state HASH contains the following fields:
38              
39             =over 4
40              
41             =item * num_files
42              
43             Total number of input files.
44              
45             =item * file_number
46              
47             Current input file number (1 to B)
48              
49             =item * file_list
50              
51             ARRAY ref. List of input filenames.
52              
53             =item * vars
54              
55             HASH ref. Empty HASH created so that any application-specific variables may be stored here.
56              
57             =item * line_num
58              
59             Current line number of line being processed (1 to N).
60              
61             =item * output_lines
62              
63             ARRAY ref. List of the output lines that are to be written to the output file (maintained by the extension).
64              
65             =item * file
66              
67             Current file name of the file being processed.
68              
69             =item * line
70              
71             String of line being processed.
72              
73             =item * output
74              
75             Special variable used by application to tell extension what to output (see L).
76              
77             =back
78              
79             The state HASH reference is passed to all 3 of the application subroutines. In addition, the input line of text is also passed
80             to the main 'app' subroutine. The interface for the subroutines is:
81              
82             =over 4
83              
84             =item B
85              
86             Called once for each input file. Called at the start of processing. Allows any setting up of variables stored in the state HASH.
87              
88             Arguments are:
89              
90             =over 4
91              
92             =item I<$app> - The application object
93              
94             =item I<$opts_href> - HASH ref to the command line options (see L and L)
95              
96             =item I<$state_href> - HASH ref to state
97              
98             =back
99              
100             =item B
101              
102             Called once for each input file. Called at the start of processing. Allows any setting up of variables stored in the state HASH.
103              
104             Arguments are:
105              
106             =over 4
107              
108             =item I<$app> - The application object
109              
110             =item I<$opts_href> - HASH ref to the command line options (see L and L)
111              
112             =item I<$state_href> - HASH ref to state
113              
114             =item I<$line> - Text of input line
115              
116             =back
117              
118             =item B
119              
120             Called once for each input file. Called at the end of processing. Allows for any end of file tidy up, data sorting etc.
121              
122             Arguments are:
123              
124             =over 4
125              
126             =item I<$app> - The application object
127              
128             =item I<$opts_href> - HASH ref to the command line options (see L and L)
129              
130             =item I<$state_href> - HASH ref to state
131              
132             =back
133              
134             =back
135              
136              
137             =head2 Output
138              
139             By default, each time the extension calls the 'app' subroutine it sets the B field of the state HASH to undef. The 'app'
140             subroutine must set this field to some value for the extension to write anything to the output file.
141              
142             For examples, the following simple 'app' subroutine causes all input files to be output uppercased:
143              
144             sub app
145             {
146             my ($app, $opts_href, $state_href, $line) = @_ ;
147            
148             # uppercase
149             $state_href->{output} = uc $line ;
150             }
151              
152             If no L option is specified, then all output will be written to STDOUT. Also, normally the output is written line-by-line after each line has been processed. If the L
153             option has been specified, then all output lines are buffered (into the state variable L) then written out at the end of processing all input. Similarly, if the L
154             option is specified, then buffering is used to process the complete input file then overwrite it with the output.
155              
156             =head2 Outfile option
157              
158             The L option may be used to set the output filename. This may include variables that are specific to the Filter extension, where the variables value is updated for each
159             input file being processed. The following Filter-sepcific variables may be used:
160              
161             $filter{'filter_file'} = $state_href->{file} ;
162             $filter{'filter_filenum'} = $state_href->{file_number} ;
163             my ($base, $path, $ext) = fileparse($file, '\..*') ;
164             $filter{'filter_name'} = $base ;
165             $filter{'filter_base'} = $base ;
166             $filter{'filter_path'} = $path ;
167             $filter{'filter_ext'} = $ext ;
168              
169             =over 4
170              
171             =item I - Input full file path
172              
173             =item I - Basename of input file (excluding extension)
174              
175             =item I - Alias for L
176              
177             =item I - Directory path of input file
178              
179             =item I - Extension of input file
180              
181             =item I - Input file number (starting from 1)
182              
183             =back
184              
185              
186             NOTE: Specifying these variables for options at the command line will require you to escape the variables per the operating system you are using (e.g. use single quotes ' ' around
187             the value in Linux).
188              
189             For example, with the command line arguments:
190              
191             -outfile '/tmp/$filter_name-$filter_filenum.txt' afile.doc /doc/bfile.text
192              
193             Processes './afile.doc' into '/tmp/afile-1.txt', and '/doc/bfile.text' into '/tmp/bfile-2.txt'
194              
195              
196             =head2 Example
197              
198             As an example, here is a script that filters one or more HTML files to strip out unwanted sections (they are actually Doxygen HTML files
199             that I wanted to convert into a pdf book):
200              
201             #!/usr/bin/perl
202             #
203             use strict ;
204             use App::Framework '::Filter' ;
205            
206             # VERSION
207             our $VERSION = '1.00' ;
208            
209             ## Create app
210             go() ;
211            
212             #----------------------------------------------------------------------
213             sub app_begin
214             {
215             my ($app, $opts_href, $state_href, $line) = @_ ;
216            
217             # force in-place editing
218             $app->set(inplace => 1) ;
219            
220             # set to start state
221             $state_href->{vars} = {
222             'state' => 'start',
223             } ;
224             }
225            
226             #----------------------------------------------------------------------
227             # Main execution
228             #
229             sub app
230             {
231             my ($app, $opts_href, $state_href, $line) = @_ ;
232            
233             my $ok = 1 ;
234             if ($state_href->{'vars'}{'state'} eq 'start')
235             {
236             if ($line =~ m/
295             **
296             **
297             **
298             ..
299             **
300             **
301            
302            

File List

Here is a list of all files with brief descriptions:
303            
src/rctu4_tests.c
304            
src/common/ate_general.c
305             ...
306            
307            
src/tests/test_star_daisychain_specific.c
308            
src/tests/test_version_functions.c
309            
310            
311            
312            
313             **
Generated on Fri Jun 5 13:43:31 2009 for rctu4_test by 
314             **
315             **doxygen 1.5.5
316            
317            
318              
319             And removes the lines beginning '**'.
320              
321             The script does in-place updating of the HTML files and can be run as:
322              
323             filter-script *.html
324              
325             =cut
326              
327 2     2   8676 use strict ;
  2         3  
  2         53  
328 2     2   7 use Carp ;
  2         3  
  2         156  
329              
330             our $VERSION = "1.001" ;
331              
332              
333              
334              
335              
336             #============================================================================================
337             # USES
338             #============================================================================================
339 2     2   9 use File::Path ;
  2         2  
  2         84  
340 2     2   6 use File::Basename ;
  2         3  
  2         90  
341 2     2   7 use File::Spec ;
  2         2  
  2         33  
342 2     2   8 use App::Framework::Core ;
  2         1  
  2         49  
343              
344              
345             #============================================================================================
346             # OBJECT HIERARCHY
347             #============================================================================================
348 2     2   388 use App::Framework::Extension ;
  2         2  
  2         2823  
349             our @ISA ;
350              
351             #============================================================================================
352             # GLOBALS
353             #============================================================================================
354              
355             =head2 ADDITIONAL COMMAND LINE OPTIONS
356              
357             This extension adds the following additional command line options to any application:
358              
359             =over 4
360              
361             =item B<-skip_empty> - Skip blanks
362              
363             Do not process empty lines (lines that contain only whitespace)
364              
365             =item B<-trim_space> - Trim spaces
366              
367             Remove spaces from start and end of lines
368              
369             =item B<-trim_comment> - Trim comments
370              
371             Remove any comments from the line, starting from the comment string to the end of the line
372              
373             =item B<-inplace> - In-place filter
374              
375             Read file, process, then overwrite original input file with processed output
376              
377             =item B<-outdir> - Specify output directory
378              
379             Write file(s) into specified directory rather that into same directory as input file
380              
381             =item B<-outfile> - Specify output file
382              
383             Specify the output filename, which may include variables (see L)
384              
385             =item B<-comment> - Specify command string
386              
387             Specify the comment start string. Used in conjuntion with L.
388              
389             =back
390              
391             =cut
392              
393             # Set of script-related default options
394             my @OPTIONS = (
395             ['skip_empty', 'Skip blanks', 'Do not process empty lines', ],
396             ['trim_space', 'Trim spaces', 'Remove spaces from start/end of line', ],
397             ['trim_comment', 'Trim comments', 'Remove comments from line'],
398             ['inplace', 'In-place filter', 'Read file, process, then overwrite input file'],
399             ['outdir=s', 'Output directory', 'Write files into specified directory (rather than into same directory as input file)'],
400             ['outfile=s', 'Output filename', 'Specify the output filename which may include variables'],
401             ['comment=s', 'Comment', 'Specify the comment start string', '#'],
402             ) ;
403              
404             =head2 COMMAND LINE ARGUMENTS
405              
406             This extension sets the following additional command line arguments for any application:
407              
408             =over 4
409              
410             =item B - Input file(s)
411              
412             Specify one of more input files to be processed. If no files are specified on the command line then reads from STDIN.
413              
414             =back
415              
416             =cut
417              
418             # Arguments spec
419             my @ARGS = (
420             ['file=f*', 'Input file(s)', 'Specify one (or more) input file to be processed']
421             ) ;
422              
423             our $class_debug=0;
424              
425             #============================================================================================
426              
427             =head2 FIELDS
428              
429             Note that the fields match with the command line options.
430              
431             =over 4
432              
433             =item B - Skip blanks
434              
435             Do not process empty lines (lines that contain only whitespace)
436              
437             =item B - Trim spaces
438              
439             Remove spaces from start and end of lines
440              
441             =item B - Trim comments
442              
443             Remove any comments from the line, starting from the comment string to the end of the line
444              
445             =item B - In-place filter
446              
447             Read file, process, then overwrite original input file with processed output
448              
449             =item B - Buffer output
450              
451             Store output lines into a buffer, then write out file at end of processing
452              
453             =item B - Specify output directory
454              
455             Write file(s) into specified directory rather that into same directory as input file
456              
457             =item B - Specify output file
458              
459             Specify the output filename, which may include variables (see L)
460              
461             =item B - Specify command string
462              
463             Specify the comment start string. Used in conjuntion with L.
464              
465             =item B - Output file handle
466              
467             Read only. File handle of current output file.
468              
469             =back
470              
471             =cut
472              
473             my %FIELDS = (
474             ## Object Data
475             'skip_empty' => 0,
476             'trim_space' => 0,
477             'trim_comment' => 0,
478             'comment' => '#',
479             'buffer' => 0,
480             'inplace' => 0,
481             'outfile' => undef,
482             'outdir' => undef,
483            
484             ## internal
485             'out_fh' => undef,
486            
487             '_filter_state' => {},
488             '_filter_opts' => undef,
489             ) ;
490              
491             #============================================================================================
492              
493             =head2 CONSTRUCTOR METHODS
494              
495             =over 4
496              
497             =cut
498              
499             #============================================================================================
500              
501             =item B
502              
503             Create a new App::Framework::Extension::Filter.
504              
505             The %args are specified as they would be in the B method, for example:
506              
507             'mmap_handler' => $mmap_handler
508              
509             The full list of possible arguments are :
510              
511             'fields' => Either ARRAY list of valid field names, or HASH of field names with default values
512              
513             =cut
514              
515             sub new
516             {
517 1     1 1 4 my ($obj, %args) = @_ ;
518              
519 1   33     10 my $class = ref($obj) || $obj ;
520              
521             ## create object dynamically
522 1         5 my $this = App::Framework::Core->inherit($class, %args) ;
523              
524             #print "Filter - $class ISA=@ISA\n" if $class_debug ;
525              
526             ## Set options
527 1         7 $this->feature('Options')->append_options(\@OPTIONS) ;
528            
529             ## Update option defaults
530 1         4 $this->feature('Options')->defaults_from_obj($this, [keys %FIELDS]) ;
531              
532             ## Set args
533 1         5 $this->feature('Args')->append_args(\@ARGS) ;
534              
535             #$this->debug(2) ;
536            
537             ## hi-jack the app function
538             $this->extend_fn(
539 6     6   29 'app_fn' => sub {$this->filter_run(@_);},
540 6     6   28 'app_start_fn' => sub {$this->_filter_start(@_);},
541 6     6   25 'app_end_fn' => sub {$this->_filter_end(@_);},
542 1         16 ) ;
543              
544 1         6 return($this) ;
545             }
546              
547              
548              
549             #============================================================================================
550              
551             =back
552              
553             =head2 CLASS METHODS
554              
555             =over 4
556              
557             =cut
558              
559             #============================================================================================
560              
561             #-----------------------------------------------------------------------------
562              
563             =item B
564              
565             Initialises the object class variables.
566              
567             =cut
568              
569             sub init_class
570             {
571 1     1 1 2 my $class = shift ;
572 1         2 my (%args) = @_ ;
573              
574             # Add extra fields
575 1         9 $class->add_fields(\%FIELDS, \%args) ;
576              
577             # init class
578 1         6 $class->SUPER::init_class(%args) ;
579              
580             }
581              
582              
583             #============================================================================================
584              
585             =back
586              
587             =head2 OBJECT METHODS
588              
589             =over 4
590              
591             =cut
592              
593             #============================================================================================
594              
595             #----------------------------------------------------------------------------
596              
597             =item B
598              
599             Filter the specified file(s) one at a time.
600            
601             =cut
602              
603              
604             sub filter_run
605             {
606 6     6 1 8 my $this = shift ;
607 6         10 my ($app, $opts_href, $args_href) = @_ ;
608              
609             ## save for later
610 6         116 $this->_filter_opts($opts_href) ;
611              
612 6         41 $this->_dbg_prt(["Args=", $args_href, "Opts=", $opts_href]) ;
613            
614             # Get command line arguments
615 6 50       9 my @args = @{ $args_href->{'file'} || [] } ;
  6         21  
616 6 50       7 my @args_fh = @{ $args_href->{'file_fh'} || [] } ;
  6         21  
617              
618             ## check for in-place editing on STDIN
619 6 100       15 if ($opts_href->{inplace})
620             {
621 1 50 33     26 if ( (scalar(@args) == 1) && ($args_fh[0] == \*STDIN) )
622             {
623 0         0 $this->throw_fatal("Cannot do in-place editing of standard input") ;
624             }
625             }
626              
627 6         18 $this->_dispatch_entry_features(@_) ;
628              
629             #$this->debug(2) ;
630              
631 6         19 $this->_dbg_prt(["#!# Hello, Ive started filter_run()...\n"]) ;
632              
633             ## Update from options
634 6         17 $this->feature('Options')->obj_vars($this, [keys %FIELDS]) ;
635              
636             ## Set up filter state
637 6         126 my $state_href = $this->_filter_state ;
638 6         15 $state_href->{num_files} = scalar(@args) ;
639 6         12 $state_href->{file_number} = 1 ;
640 6         15 $state_href->{file_list} = \@args ;
641 6         18 $state_href->{vars} = {} ;
642              
643             ## do each file
644 6         23 for (my $fnum=0; $fnum < $state_href->{num_files}; ++$fnum)
645             {
646              
647 6         15 $state_href->{file_number} = $fnum+1 ;
648 6         11 $state_href->{outfile} = '' ;
649 6         10 $state_href->{line_num} = 1 ;
650 6         11 $state_href->{output_lines} = [] ;
651 6         55 $state_href->{file} = $args[$fnum] ;
652              
653 6         35 $this->_dispatch_label_entry_features('file', $app, $opts_href, $state_href) ;
654            
655 6         20 $this->_start_output($state_href, $opts_href) ;
656            
657             ## call application start
658 6         36 $this->call_extend_fn('app_start_fn', $state_href) ;
659              
660             ## Process file
661 6         57 my $fh = $args_fh[$fnum] ;
662 6         7 my $line ;
663 6         193 while(defined($line = <$fh>))
664             {
665 264         239 chomp $line ;
666            
667             ## see if line needs processing
668 264 100       357 if ($opts_href->{trim_space})
669             {
670 132         187 $line =~ s/^\s+// ;
671 132         270 $line =~ s/\s+$// ;
672             }
673 264 50 66     453 if ($opts_href->{trim_comment} && $opts_href->{comment})
674             {
675 132         371 $line =~ s/$opts_href->{comment}.*$// ;
676             }
677            
678 264         243 $state_href->{line} = $line ;
679 264         216 $state_href->{output} = undef ;
680            
681 264         497 $this->_dispatch_label_entry_features('line', $app, $opts_href, $state_href) ;
682              
683             ## see if we skip this line
684 264         209 my $skip = 0 ;
685 264 100       356 if ($opts_href->{skip_empty})
686             {
687 132 100       327 $skip=1 if $line =~ m/^\s*$/ ;
688             }
689              
690             ## call application (if not skipped)
691 264 100       588 $this->call_extend_fn('app_fn', $state_href, $line) unless $skip ;
692            
693 264         1307 $this->_handle_output($state_href, $opts_href) ;
694              
695 264         242 $state_href->{line_num}++ ;
696              
697 264         525 $this->_dispatch_label_exit_features('line', $app, $opts_href, $state_href) ;
698             }
699 6         290 close $fh ;
700              
701             ## call application end
702 6         29 $this->call_extend_fn('app_end_fn', $state_href) ;
703              
704 6         25 $this->_end_output($state_href, $opts_href) ;
705              
706             # $state_href->{file_number}++ ;
707              
708 6         29 $this->_dispatch_label_exit_features('file', $app, $opts_href, $state_href) ;
709             }
710              
711 6         25 $this->_dispatch_exit_features(@_) ;
712              
713             }
714              
715              
716             #----------------------------------------------------------------------------
717             # start
718             sub _filter_start
719             {
720 6     6   15 my $this = shift ;
721 6         18 my ($app, $opts_href, @args) = @_ ;
722              
723             ## Do nothing
724              
725             }
726              
727             #----------------------------------------------------------------------------
728             # end
729             sub _filter_end
730             {
731 6     6   12 my $this = shift ;
732 6         13 my ($app, $opts_href, @args) = @_ ;
733              
734             ## Do nothing
735             }
736              
737             #----------------------------------------------------------------------------
738              
739             =item B
740              
741             Application interface for writing out extra lines
742            
743             =cut
744              
745              
746             sub write_output
747             {
748 0     0 1 0 my $this = shift ;
749 0         0 my ($output) = @_ ;
750            
751 0         0 my $state_href = $this->_filter_state ;
752 0         0 $state_href->{'output'} = $output ;
753            
754 0         0 $this->_handle_output($state_href, $this->_filter_opts) ;
755             }
756              
757              
758             # ============================================================================================
759             # PRIVATE METHODS
760             # ============================================================================================
761              
762             #----------------------------------------------------------------------------
763              
764             =item B<_start_output($state_href, $opts_href)>
765              
766             Start of output file
767            
768             =cut
769              
770              
771             sub _start_output
772             {
773 6     6   9 my $this = shift ;
774 6         10 my ($state_href, $opts_href) = @_ ;
775              
776 6         21 $this->set('out_fh' => undef) ;
777              
778 6         23 $this->_dbg_prt(["_start_output\n"]) ;
779            
780             ## do nothing if buffering or in-place editing
781 6 100 66     131 return if ($this->buffer || $this->inplace) ;
782              
783 5         19 $this->_dbg_prt([" + not buffering/inplace\n"]) ;
784              
785             # open output file (and set up output dir)
786 5         22 $this->_open_output($state_href, $opts_href) ;
787            
788             }
789              
790             #----------------------------------------------------------------------------
791              
792             =item B<_handle_output($state_href, $opts_href)>
793              
794             Write out line (if required)
795            
796             =cut
797              
798              
799             sub _handle_output
800             {
801 264     264   211 my $this = shift ;
802 264         276 my ($state_href, $opts_href) = @_ ;
803              
804             ## buffer line(s)
805 264         203 my $out = $state_href->{output} ;
806 264         492 $this->_dbg_prt(["_handle_output : output=", $out, "\n"]) ;
807 264 100       421 push @{$state_href->{output_lines}}, $out if defined($out) ;
  217         354  
808              
809             ## do nothing if buffering or in-place editing
810 264 100 66     4632 return if ($this->buffer || $this->inplace) ;
811              
812 220         469 $this->_dbg_prt([" + not buffering/inplace\n"]) ;
813              
814             ## ok to write
815 220 100       518 $this->_wr_output($state_href, $opts_href, $out) if defined($out) ;
816             }
817              
818              
819             #----------------------------------------------------------------------------
820              
821             =item B<_end_output($state_href, $opts_href)>
822              
823             End of output file
824            
825             =cut
826              
827              
828             sub _end_output
829             {
830 6     6   9 my $this = shift ;
831 6         9 my ($state_href, $opts_href) = @_ ;
832              
833 6         111 $this->_dbg_prt(["_end_output : buffer=", $this->buffer, ", inplace=", $this->inplace, ", # lines=", scalar(@{$state_href->{output_lines}}),"\n"]) ;
  6         27  
834              
835             ## if buffering or in-place editing, now need to write file
836 6 100 66     116 if ($this->buffer || $this->inplace)
837             {
838 1         4 $this->_dbg_prt([" + writing\n"]) ;
839              
840             # open output file (and set up output dir)
841 1         4 $this->_open_output($state_href, $opts_href) ;
842              
843 1         1 foreach my $line (@{$state_href->{output_lines}})
  1         8  
844             {
845 28         64 $this->_wr_output($state_href, $opts_href, $line) ;
846             }
847             }
848            
849             # close output file
850 6         24 $this->_close_output($state_href, $opts_href) ;
851             }
852              
853              
854              
855             #----------------------------------------------------------------------------
856              
857             =item B<_open_output($state_href, $opts_href)>
858              
859             Open the file (or STDOUT) depending on settings
860            
861             =cut
862              
863              
864             sub _open_output
865             {
866 6     6   10 my $this = shift ;
867 6         11 my ($state_href, $opts_href) = @_ ;
868              
869 6         17 $this->set('out_fh' => undef) ;
870              
871 6         22 $this->_dbg_prt(["_open_output\n"]) ;
872            
873 6         14 my $outfile ;
874 6 100       116 if ($this->inplace)
    50          
875             {
876             ## Handle in-place editing
877 1         3 $outfile = $state_href->{file} ;
878 1         7 $this->_dbg_prt([" + inplace file=$outfile\n"]) ;
879             }
880             elsif ($this->outfile)
881             {
882             ## See if writing to dir
883 5         89 my $dir = $this->outdir ;
884 5 50       12 if ($dir)
885             {
886             ## create path
887 0         0 mkpath([$dir], $this->debug, 0755) ;
888             }
889 5   50     18 $dir ||= '.' ;
890            
891 5         16 my %opts = $this->options() ;
892 5         25 my %app_vars = $this->vars() ;
893 5         20 my %filter ;
894 5         88 $filter{'filter_fmt'} = $this->outfile ;
895 5         12 $filter{'filter_file'} = $state_href->{file} ;
896 5         8 $filter{'filter_filenum'} = $state_href->{file_number} ;
897 5         227 my ($base, $path, $ext) = fileparse($state_href->{file}, '\..*') ;
898 5         12 $filter{'filter_name'} = $base ;
899 5         9 $filter{'filter_base'} = $base ;
900 5         8 $filter{'filter_path'} = $path ;
901 5         11 $filter{'filter_ext'} = $ext ;
902              
903 5         25 $this->expand_keys(\%filter, [\%opts, \%app_vars, \%ENV]) ;
904            
905 5         10 $outfile = $filter{'filter_fmt'} ;
906            
907 5         22 $this->_dbg_prt([" + eval=$@\n"]) ;
908 5         35 $this->_dbg_prt([" + outfile=$outfile: dir=$dir fmt=$filter{'filter_fmt'} file=$filter{'filter_file'} num=$filter{'filter_filenum'} base=$base path=$path\n"]) ;
909            
910 5         122 $outfile = File::Spec->catfile($dir, $outfile) ;
911             }
912            
913             ## Output file specified?
914 6 50       17 if ($outfile)
915             {
916 6         209 $outfile = File::Spec->rel2abs($outfile) ;
917 6         51 my $infile = File::Spec->rel2abs($state_href->{file}) ;
918              
919 6 100       15 if ($outfile eq $infile)
920             {
921             # In place editing - make sure flag is set
922 1         20 $this->inplace(1) ;
923              
924 1         5 $this->_dbg_prt([" + inplace $outfile\n"]) ;
925             }
926              
927             # else
928             # {
929             ## Open output
930 6 50       931 open my $outfh, ">$outfile" or $this->throw_fatal("Unable to write \"$outfile\" : $!") ;
931 6         157 $this->out_fh($outfh) ;
932              
933 6         43 $this->_dbg_prt([" + opened $outfile fh=$outfh\n"]) ;
934            
935 6         18 $state_href->{outfile} = $outfile ;
936             # }
937            
938             }
939             else
940             {
941             ## STDOUT
942 0         0 $this->out_fh(\*STDOUT) ;
943             }
944             }
945              
946             #----------------------------------------------------------------------------
947              
948             =item B<_close_output($state_href, $opts_href)>
949              
950             Close the file if open
951            
952             =cut
953              
954              
955             sub _close_output
956             {
957 6     6   8 my $this = shift ;
958 6         9 my ($state_href, $opts_href) = @_ ;
959              
960 6         110 my $fh = $this->out_fh ;
961 6         29 $this->set('out_fh' => undef) ;
962            
963 6 50       105 if ($this->outfile)
964             {
965 6         395 close $fh ;
966             }
967             else
968             {
969             ## STDOUT - so ignore
970             }
971             }
972              
973             #----------------------------------------------------------------------------
974              
975             =item B<_wr_output($state_href, $opts_href, $line)>
976              
977             End of output file
978            
979             =cut
980              
981              
982             sub _wr_output
983             {
984 217     217   151 my $this = shift ;
985 217         213 my ($state_href, $opts_href, $line) = @_ ;
986              
987 217         3556 my $fh = $this->out_fh ;
988              
989 217         631 $this->_dbg_prt(["_wr_output($line) fh=$fh\n"]) ;
990 217 50       339 if ($fh)
991             {
992 217         521 print $fh "$line\n" ;
993             }
994             }
995              
996              
997             # ============================================================================================
998             # END OF PACKAGE
999              
1000             =back
1001              
1002             =head1 DIAGNOSTICS
1003              
1004             Setting the debug flag to level 1 prints out (to STDOUT) some debug messages, setting it to level 2 prints out more verbose messages.
1005              
1006             =head1 AUTHOR
1007              
1008             Steve Price C<< >>
1009              
1010             =head1 BUGS
1011              
1012             None that I know of!
1013              
1014             =cut
1015              
1016             1;
1017              
1018             __END__