File Coverage

blib/lib/Filter/Heredoc/App.pm
Criterion Covered Total %
statement 29 312 9.2
branch 0 162 0.0
condition 0 66 0.0
subroutine 10 25 40.0
pod 1 1 100.0
total 40 566 7.0


line stmt bran cond sub pod time code
1             package Filter::Heredoc::App;
2              
3 1     1   1151 use 5.010;
  1         3  
4 1     1   5 use strict;
  1         2  
  1         30  
5 1     1   3 use warnings;
  1         2  
  1         42  
6              
7             our $VERSION = '0.05';
8              
9             =head1 NAME
10              
11             Filter::Heredoc::App - The module behind the filter-heredoc command
12              
13             =head1 VERSION
14              
15             Version 0.05
16              
17             =cut
18              
19 1     1   5 use base qw( Exporter );
  1         1  
  1         103  
20             our @EXPORT_OK = qw ( run_filter_heredoc );
21              
22 1     1   5 use Filter::Heredoc qw( hd_getstate hd_init hd_labels );
  1         2  
  1         59  
23 1     1   5 use Filter::Heredoc::Rule qw( hd_syntax );
  1         2  
  1         50  
24 1     1   10 use File::Basename qw( basename );
  1         2  
  1         57  
25 1     1   360 use POSIX qw( strftime );
  1         4838  
  1         4  
26 1     1   1674 use Getopt::Long;
  1         9937  
  1         5  
27 1     1   150 use Carp;
  1         2  
  1         2509  
28              
29             our $SCRIPT = basename($0);
30              
31             our @gl_delimiterarray = (); # delimiters to match
32             our @gl_to_be_unique_delimiters = (); # (to be) unique delimiters
33             our $gl_is_successful_match = 0; # successful match flag
34              
35             ### Export_ok subroutines starts here ###
36              
37             ### INTERFACE SUBROUTINE ###
38             # Usage : run_filter_heredoc()
39             # Purpose : This module implements the logic and functions to
40             # search and filter here documents in scripts.
41             # This is the code behind the filter-heredoc command.
42             # Return : Normally returns to caller or dies with exit code 0.
43             # Errors : Dies with exit code 1 on user errors. Dies with exit
44             # code 2 on internal errors in hd_getstate().
45             # Throws : No.
46              
47             sub run_filter_heredoc {
48              
49 0     0 1   my $linestate;
50             my %state;
51 0           my $EMPTY_STR = q{};
52 0           my $PROMPT = q{> };
53 0           my $POD = q{pod};
54 0   0       my $is_interactive = ( ( -t STDIN ) && ( -t STDOUT ) );
55 0           my $is_use_prompt = $EMPTY_STR;
56 0           my $is_with_fileinfo = $EMPTY_STR;
57              
58 0           my %user_warning = (
59             firstexclusive =>
60             q{Options --help(-h), --version(-v) or --rules(-r) can't be specfied at the same time.},
61             exclusive =>
62             q{Options --list(-l), --list-unique(-u), --debug(-d) or '-' can't be specfied at the same time.},
63             delimiters =>
64             q{Options --list(-l), --list-unique(-u) or --match(-m) can't be specfied at the same time.},
65             filename =>
66             q{Missing any file arguments. Type --help for information.},
67             notextfile =>
68             q{Please try again, and limit the files to readable text files.},
69             );
70              
71             # Flush all state arays and activate syntax rule 'pod' as default
72 0           hd_init();
73 0           hd_syntax($POD);
74              
75             # Configure long options handling
76 0           Getopt::Long::Configure("no_auto_abbrev")
77             ; # must spell out full long option
78 0           Getopt::Long::Configure("bundling"); # bundle the short options
79 0           Getopt::Long::Configure("no_ignore_case"); # use correct case
80              
81             ##############################################################
82             # Do some internal checks before @ARGV is shifted by Getopt
83             ##############################################################
84              
85             # Have we the mandatory filename argument on the command line. In
86             # a pipe or redirect, @ARGV is empty, therefore '$is_interactive'.
87 0 0 0       if ( ($is_interactive) && ( $#ARGV == -1 ) ) {
88 0           _print_to_stderr_exit( \$user_warning{filename} );
89             }
90              
91             # Test and prepare possible for line-by-line interactive mode.
92 0           $is_use_prompt = _is_lone_cli_dash(@ARGV);
93              
94             ##############################################################
95             # Getopt to decode our command line arguments
96             ##############################################################
97              
98 0           my ($is_help, $is_version, $is_quiet, $is_rules,
99             $is_debug, $is_list, $is_unique
100             ) = ( 0, 0, 0, 0, 0, 0, 0 );
101 0           my ( $syntax, $match ) = ( $EMPTY_STR, $EMPTY_STR );
102              
103 0           my $options_okay = GetOptions(
104             "h|help" => \$is_help,
105             "v|version" => \$is_version,
106             "q|quiet" => \$is_quiet,
107             "d|debug" => \$is_debug,
108             "i|interactive" => \$is_use_prompt,
109             "r|rules" => \$is_rules,
110             "l|list" => \$is_list,
111             "u|list-unique" => \$is_unique,
112             "s|syntax=s" => \$syntax,
113             "m|match=s" => \$match,
114             );
115              
116             ##############################################################
117             # Getopt done.
118             ##############################################################
119              
120 0 0         _help() if !$options_okay;
121              
122             ########################################
123             # Exclusive do-and-exit-options: --version, --help, and --rules
124 0           my $useroptions = 0;
125 0           foreach ( $is_help, $is_version, $is_rules ) {
126 0 0         $useroptions++ if $_;
127             }
128             _print_to_stderr_exit( \$user_warning{firstexclusive} )
129 0 0         if ( $useroptions > 1 );
130              
131             ########################################
132             # Execute the do-and-exit options first
133 0 0         if ($is_help) {
    0          
    0          
134 0           _print_help();
135             }
136             elsif ($is_version) {
137 0           _print_version();
138             }
139             elsif ($is_rules) {
140 0           _print_rules();
141             }
142              
143             ########################################
144             # Populate the global array of target delimiters to match
145 0 0         if ($match) {
146 0           _set_match_delimiters($match);
147             }
148             ########################################
149             # Exclusive options tests
150             # Test --list, --list-unique, --debug and '-'
151 0           $useroptions = 0;
152 0           foreach ( $is_list, $is_debug, $is_use_prompt, $is_unique ) {
153 0 0         $useroptions++ if $_;
154             }
155 0 0         _print_to_stderr_exit( \$user_warning{exclusive} ) if ( $useroptions > 1 );
156              
157             # --list or --list-unique (prints all delimiters)
158             # and --match (use specific delimiter) is mutually exclusive.
159 0 0 0       if ( $is_list || $is_unique ) {
160              
161             # Array contains elements, e.g. '--match=eof,eot' is given
162 0 0 0       if ( ( $#gl_delimiterarray >= 0 ) && ($match) ) {
163 0           _print_to_stderr_exit( \$user_warning{delimiters} );
164             }
165             }
166              
167             # Should we switch to interactive mode (here line-by-line input)
168 0 0         if ($is_use_prompt) {
169 0 0         if ( !$is_interactive ) {
170 0           _help();
171             }
172             else {
173 0           print "$SCRIPT: Line by line input - use Ctrl-D to quit\n";
174 0           print $PROMPT;
175             }
176             }
177              
178             # Again! Test that we have the mandatory filename arguments
179             # Getopt have mangled the @ARGV content after removing options.
180 0 0         if ( !$is_use_prompt ) {
181             _print_to_stderr_exit( \$user_warning{filename} )
182 0 0 0       if ( $is_interactive && ( $#ARGV == -1 ) );
183             }
184              
185             ###########################################################
186             # Before we let <> loose, sanitize the file list in @ARGV.
187             # Only allow text files, and exit if not.
188             ###########################################################
189              
190             # Shell have already expanded '*' in @ARGV to file list
191 0           my @files = @ARGV;
192 0           my @text_files = ();
193              
194 0 0 0       if ( ( $#files != -1 ) && ( !_is_lone_cli_dash(@ARGV) ) ) {
195 0           my @no_good_files;
196 0           my $exit_now_flag = 0;
197              
198 0           foreach ( 0 .. $#files ) {
199              
200 0 0         if ( !-e $files[$_] ) {
201 0           print STDERR
202             "$SCRIPT: cannot access '$files[$_]': Can not access file. Does it exist?\n";
203 0           exit(1);
204             }
205              
206             # exclude directories
207 0 0         if ( -d $files[$_] ) {
    0          
208 0           next;
209             }
210              
211             # readable by effective user id, plain file, and text
212             elsif ( -r -f -T $files[$_] ) {
213 0           push @text_files, $files[$_];
214             }
215             else {
216 0           $exit_now_flag = 1;
217 0           push @no_good_files, $files[$_];
218             }
219              
220             }
221 0 0         if ($exit_now_flag) {
222              
223 0 0         if (@text_files) {
224 0           print STDERR "$SCRIPT: These may be acceptable text files:\n";
225 0           foreach my $item (@text_files) {
226 0           print STDERR "'$item', ";
227             }
228 0           print STDERR "\n";
229             }
230 0           print STDERR
231             "$SCRIPT: These are not plain text files or are not accessible (maybe links):\n";
232 0           foreach my $item (@no_good_files) {
233 0           print STDERR "'$item', ";
234             }
235 0           print STDERR "\n$SCRIPT: ";
236 0           my $allstderrprintdone = 1;
237              
238             _print_to_stderr_exit( \$user_warning{notextfile} )
239 0 0         if ($allstderrprintdone);
240              
241             } # end exit_now_flag_flag
242             }
243              
244             # Last time test that we have the mandatory filename arguments after
245             # that we mangled the file list @text_files content.
246 0 0         if ( !$is_use_prompt ) {
247             _print_to_stderr_exit( \$user_warning{filename} )
248 0 0 0       if ( $is_interactive && ( $#text_files == -1 ) );
249             }
250              
251             ###########################################################
252             # Set our syntax if given any
253 0 0         if ($syntax) {
254 0           _set_syntax_rules($syntax);
255             }
256              
257             ###########################################################
258             # Main loop processing one line after line from STDIN
259             ###########################################################
260 0           while ( defined( my $line = ) ) {
261              
262             # print all here-doc delimiters (i.e. '--list' or '--list-unique')
263 0 0 0       if ( ( $is_list || $is_unique ) && ( !$is_use_prompt ) ) {
    0 0        
264              
265 0 0         if ( !$is_quiet ) {
266 0           $is_with_fileinfo = 1; # adds file information
267             }
268 0           _print_all_delimiters( $line, $is_with_fileinfo, $is_unique );
269              
270             }
271              
272             # end --list, --list-unique options (print all here-doc delimiters)
273              
274             elsif ( !$is_use_prompt ) {
275              
276             ### print here-doc content (default without any options)
277 0           my $is_add_label;
278 0 0         if ( !$is_quiet ) {
279 0           $is_with_fileinfo = 1; # file information when printing
280             }
281              
282             # print all lines for debug incl state code (i.e. --debug option)
283 0 0         if ($is_debug) {
284 0           $is_add_label = 1;
285 0           _debug_every_line( $line, $is_with_fileinfo, $is_add_label );
286             }
287              
288             # print only the embedded here document lines (default option)
289             else {
290 0           $is_add_label = $EMPTY_STR;
291 0           _print_heredoc( $line, $is_with_fileinfo, $is_add_label );
292             }
293             ### end print here-doc content
294              
295             }
296              
297             # Inter-active and no cmd line arguments.
298 0 0         if ($is_use_prompt) {
    0          
299              
300             ############## If exception exit(2) ########
301 0           eval { %state = hd_getstate($line); };
  0            
302 0 0         if ($@) {
303 0           my $logcreated = _write_error_file( $@, _get_error_fname() );
304 0 0         if ($logcreated) {
305 0           print STDERR "Fatal internal errors, see file:",
306             _get_error_fname(), "\n";
307             }
308 0           exit(2);
309             }
310             ############################################
311 0           print "$state{statemarker}]$line";
312              
313 0           print $PROMPT; # We can test the script with Test::Expect
314             }
315             elsif (eof) {
316              
317             # --list-unique
318 0 0         if ($is_unique) {
319 0 0         if ($is_quiet) {
320 0           _print_unique_delimiters($EMPTY_STR);
321             }
322             else {
323 0           _print_unique_delimiters($ARGV); # current file name
324             }
325 0           print "\n"; # new line after each file
326             }
327              
328             # --match
329 0 0         if ($match) {
330 0 0         if ( !$gl_is_successful_match ) {
331 0 0         print "($ARGV)" unless ($is_quiet);
332 0           print
333             "Sorry, no here document content matched your delimiter(s): '$match'. Try --list.\n";
334             }
335              
336 0           $gl_is_successful_match = $EMPTY_STR; # False
337             }
338              
339              
340 0           close(ARGV);
341 0           hd_init()
342             ; # re-init state explicitely to flush state possible errors
343             }
344              
345             # end inter-active and no cmd line arguments
346              
347             }
348              
349 0           print "\n"; # print one LF before returning to caller script and exit.
350 0           return;
351              
352             }
353              
354             ### The Module private subroutines starts here ###
355              
356             ### INTERNAL UTILITY ###
357             # Usage : _print_help()
358             # Purpose : Print command line help
359             # Returns : No, dies with exit code 0
360             # Throws : No
361              
362             sub _print_help {
363              
364 0     0     print <<"END_USAGE";
365            
366             $SCRIPT: Filter embedded here-documents in scripts
367            
368             Usage:
369              
370             $SCRIPT [options] file
371             $SCRIPT [options] < file
372             cat file | $SCRIPT [options] | program
373              
374             file: Source script file with embedded here-documents
375             program: Program to receive input from $SCRIPT output
376              
377             Options
378              
379             --list,-l : list all delimiters and exit.
380             --list-unique,-u : list only unique delimiters and exit.
381             --match=,-m : print only here-documents matching the delimiters.
382             --quiet,-q : supress file information.
383              
384             --rules,-r : list available rules and exit.
385             --syntax=,-s : add specified rule(s).
386            
387             --help,-h : show this help and exit.
388             --version,-v : print $SCRIPT version information and exit.
389             --debug,-d : print all script lines, not only here-document lines.
390             --interactive,-i|- : enter text line-by-line (for state debugging).
391            
392             Type 'perldoc $SCRIPT' for more information.
393            
394             END_USAGE
395              
396 0           exit(0);
397              
398             }
399              
400             ### INTERNAL UTILITY ###
401             # Usage : _print_version()
402             # Purpose : Print version, copyright and disclaimer
403             # Returns : No, dies with exit code 0
404             # Throws : No
405              
406             sub _print_version {
407              
408 0     0     print <<"END_VERSION";
409             $SCRIPT, version $VERSION
410             Copyright 2011-2018, Bertil Kronlund
411            
412             This program is free software; you can redistribute it and/or modify it
413             under the terms of either: the GNU General Public License as published
414             by the Free Software Foundation; or the Artistic License.
415              
416             See http://dev.perl.org/licenses/ for more information.
417            
418             THIS PACKAGE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR
419             IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
420             WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
421              
422             END_VERSION
423              
424 0           exit(0);
425              
426             }
427              
428             ### INTERNAL UTILITY ###
429             # Usage : _print_rules()
430             # Purpose : print the available syntax rules
431             # Returns : No, dies with exit code 0
432             # Throws : No
433              
434             sub _print_rules {
435 0     0     my $EMPTY_STR = q{};
436 0           my $value = shift;
437 0           my %syntax;
438              
439 0 0         if ( !defined $value ) {
440              
441             # Request rule capabilities
442 0           %syntax = hd_syntax();
443              
444 0           print "Available options to use with --syntax option: ";
445 0           foreach ( keys %syntax ) {
446 0           print "'$_' ";
447 0 0         if ( $syntax{$_} ne $EMPTY_STR ) {
448 0           print '(active) ';
449             }
450             }
451              
452 0           print "\n";
453 0           exit(0);
454             }
455              
456             }
457              
458             ### INTERNAL UTILITY ###
459             # Usage : _set_syntax_rules()
460             # Purpose : Sets the syntax rule during this run.
461             # Returns : Normally returns to caller or dies
462             # with exit code 1 on user error.
463             # Throws : No
464              
465             sub _set_syntax_rules {
466 0     0     my $EMPTY_STR = q{};
467 0           my $NONE = q{none};
468 0           my $rule = shift;
469 0           my %syntax;
470              
471 0 0         if ( $rule =~ m/^-/xsm ) {
472 0           print "Invalid --syntax argument option: '$rule'\n";
473 0           exit(1);
474             }
475             else {
476              
477             # Try to set given rule
478 0           $rule = lc($rule); # Ignore case
479 0           %syntax = hd_syntax($rule);
480              
481             # if rule is word 'none' (i.e flush all rules) we are done
482 0 0         if ( $rule ne $NONE ) {
483              
484             # Was this a non-existent key?
485 0 0         if ( !exists $syntax{$rule} ) {
    0          
486 0           print "Invalid syntax rule option: '$rule'. ",
487             "Try option --rules to view all available.\n";
488 0           exit(1);
489             }
490              
491             # Was the change applied
492             elsif ( $syntax{$rule} eq $EMPTY_STR ) {
493 0           print "Sorry, could not add new rule: '$rule'. ",
494             "Try option --rules to view all available.\n";
495 0           exit(1);
496             }
497             }
498              
499             }
500 0           return;
501             }
502              
503             ### INTERNAL UTILITY ###
504             # Usage : _set_match_delimiters ( $delimiters )
505             # Purpose : Populate the global delimiters array with
506             # the delimiters to match. (option --match)
507             # Returns : Normally returns to caller or dies
508             # with exit code 1 on user error.
509             # Throws : No
510              
511             sub _set_match_delimiters {
512 0     0     my $EMPTY_STR = q{};
513 0           my $value = shift;
514              
515 0 0         if ( $value eq $EMPTY_STR ) {
    0          
516 0           print "No matching delimiter specified!\n";
517 0           exit(1);
518             }
519             elsif ( $value =~ m/^-/xsm ) {
520 0           print "Invalid --match argument option: '$value'\n";
521 0           exit(1);
522             }
523              
524             # $value contains a comma separated string of delimiters
525 0           chomp $value;
526              
527             # Assign our global array with the delimiters
528 0           @gl_delimiterarray = split( ',', $value );
529              
530 0           return;
531              
532             }
533              
534             ### INTERNAL UTILITY ###
535             # Usage : _print_to_stderr_exit()
536             # Purpose : Print user errors and die.
537             # Returns : No, dies with exit code 1
538             # Throws : No
539              
540             sub _print_to_stderr_exit {
541              
542 0     0     my $href_errmsg = shift;
543 0           print STDERR "$$href_errmsg \n";
544 0           exit(1);
545              
546             }
547              
548             ### INTERNAL UTILITY ###
549             # Usage : _is_lone_cli_dash()
550             # Purpose : Test if @ARGV contains the lone dash ('-').
551             # Returns : True (1) if found, otherwise $EMPTY_STR
552             # Throws : No
553              
554             sub _is_lone_cli_dash {
555              
556 0     0     my @cmdlinearray = @ARGV;
557 0           my $EMPTY_STR = q{};
558              
559 0           my $regex = qr/(\s*-\s*)/; # try to match '-'
560              
561 0           foreach ( 0 .. $#cmdlinearray ) {
562              
563 0 0         if ( $cmdlinearray[$_] =~ $regex ) {
564              
565             # Nothing before and after
566 0 0 0       if ( ( $` eq $EMPTY_STR ) && ( $' eq $EMPTY_STR ) ) {
567              
568             # Found the lone dash
569 0           return 1;
570             }
571             }
572             }
573              
574 0           return $EMPTY_STR;
575             }
576              
577             ### INTERNAL UTILITY ###
578             # Usage : _print_all_delimiters( $line, IS_FILEINFO, IS_UNIQUE )
579             # Purpose : Print the delimiters only. Handles options
580             # --list and --list-unique. The 2nd argument
581             # '$is_with_fileinfo' only apply to --list.
582             # Returns : N/A. Normally returns to caller.
583             # Errors : Dies with exit code 2, internal errors in hd_getstate().
584             # Throws : No
585              
586             sub _print_all_delimiters {
587              
588 0     0     my ( $line, $is_with_fileinfo, $is_unique_list ) = @_;
589 0           my $EMPTY_STR = q{};
590 0           my %state;
591              
592             # Read out the default state label symbols
593 0           my %label = hd_labels();
594              
595             ############## If exception exit(2) ########
596 0           eval { %state = hd_getstate($line); };
  0            
597 0 0         if ($@) {
598 0           my $logcreated = _write_error_file( $@, _get_error_fname() );
599 0 0         if ($logcreated) {
600 0           print STDERR "Fatal internal errors, see file:",
601             _get_error_fname(), "\n";
602             }
603 0           exit(2);
604             }
605             ############################################
606              
607 0 0         if ( $state{blockdelimiter} ne $EMPTY_STR ) {
608              
609             # The delimiter is the terminator on the egress line ('E')
610 0 0         if ( $state{statemarker} eq $label{egress} ) {
611              
612             # Option --list-unique
613 0 0         if ($is_unique_list) {
614              
615             # Add all, will become unique in _print_unique_delimiters()
616 0           push @gl_to_be_unique_delimiters, $state{blockdelimiter};
617             }
618              
619             # Option --list
620             else {
621              
622             # File information not available is pipe or redirect
623 0 0         if ( $ARGV =~ m/^-/xsm ) {
624 0           $is_with_fileinfo = $EMPTY_STR;
625             }
626              
627             # Print the delimiter itself (stored in 'blockdelimiter')
628 0 0         if ($is_with_fileinfo) {
629 0           print "($ARGV:$.)$state{blockdelimiter} \n";
630             }
631             else {
632 0           print "$state{blockdelimiter} \n";
633             }
634             }
635              
636             } # end the last delimiter (at egress)
637              
638             } # end delimiter found in block
639              
640 0           return;
641             }
642              
643             ### INTERNAL UTILITY ###
644             # Usage : _debug_every_line( $line, IS_FILEINFO, IS_ADDLABEL )
645             # Purpose : Print every line for debugging purpose.
646             # Handles option --debug
647             # Returns : N/A. Normally returns to caller.
648             # Errors : Dies with exit code 2, internal errors in hd_getstate().
649             # Throws : No
650              
651             sub _debug_every_line {
652              
653 0     0     my ( $line, $is_with_fileinfo, $is_add_label ) = @_;
654 0           my $EMPTY_STR = q{};
655 0           my %state;
656              
657             ############## If exception exit(2) ########
658 0           eval { %state = hd_getstate($line); };
  0            
659 0 0         if ($@) {
660 0           my $logcreated = _write_error_file( $@, _get_error_fname() );
661 0 0         if ($logcreated) {
662 0           print STDERR "Fatal internal errors, see file:",
663             _get_error_fname(), "\n";
664             }
665 0           exit(2);
666             }
667             ############################################
668              
669             # File information not available is pipe or redirect
670 0 0         if ( $ARGV =~ m/^-/xsm ) {
671 0           $is_with_fileinfo = $EMPTY_STR;
672             }
673              
674             SWITCH: {
675 0 0 0       ( $is_add_label && $is_with_fileinfo ) and do {
  0            
676 0           print "($ARGV:$.)$state{statemarker}]$line";
677 0           last SWITCH;
678             };
679 0 0 0       ( !$is_add_label && $is_with_fileinfo ) and do {
680 0           print "($ARGV:$.)$line";
681 0           last SWITCH;
682             };
683 0 0 0       ( $is_add_label && !$is_with_fileinfo ) and do {
684 0           print "$state{statemarker}]$line";
685 0           last SWITCH;
686             };
687 0 0 0       ( !$is_add_label && !$is_with_fileinfo ) and do {
688 0           print "$line";
689 0           last SWITCH;
690             };
691              
692             }; # switch and combine all variants
693              
694 0           return;
695             }
696              
697             ####################################################
698             # Usage: _print_heredoc();
699             # Purpose: Print (and match lines if set) here document content
700             # Returns: N/A
701             # Parameters: line to analyze, and Getopt boolens
702             # Throws: Yes
703              
704             sub _print_heredoc {
705              
706 0     0     my ( $line, $is_with_fileinfo, $is_add_code ) = @_;
707 0           my $EMPTY_STR = q{};
708              
709             # Read out the default markers symbols
710 0           my %label = hd_labels();
711 0           my %state;
712              
713             ############## If exception exit(2) ########
714 0           eval { %state = hd_getstate($line); };
  0            
715 0 0         if ($@) {
716 0           my $logcreated = _write_error_file( $@, _get_error_fname() );
717 0 0         if ($logcreated) {
718 0           print STDERR "Fatal internal errors, see file:",
719             _get_error_fname(), "\n";
720             }
721 0           exit(2);
722             }
723             ############################################
724              
725 0 0         if ( $state{statemarker} eq $label{heredoc} ) {
726              
727             # File information not available is pipe or redirect
728 0 0         if ( $ARGV =~ m/^-/xsm ) {
729 0           $is_with_fileinfo = $EMPTY_STR;
730             }
731              
732             # Print only here document matching the set delimiters
733 0 0         if (@gl_delimiterarray) {
734              
735 0           foreach my $lineitem (@gl_delimiterarray) {
736              
737 0 0         if ( $state{blockdelimiter} eq $lineitem ) {
738              
739 0           $gl_is_successful_match = 1; # True
740              
741             SWITCH: {
742 0 0 0       ( $is_add_code && $is_with_fileinfo ) and do {
  0            
743 0           print "($ARGV:$.)$state{statemarker}]$line";
744 0           last SWITCH;
745             };
746 0 0 0       ( !$is_add_code && $is_with_fileinfo ) and do {
747 0           print "($ARGV:$.)$line";
748 0           last SWITCH;
749             };
750 0 0 0       ( $is_add_code && !$is_with_fileinfo ) and do {
751 0           print "$state{statemarker}]$line";
752 0           last SWITCH;
753             };
754 0 0 0       ( !$is_add_code && !$is_with_fileinfo ) and do {
755 0           print "$line";
756 0           last SWITCH;
757             };
758              
759             }; # switch and combine all variants
760              
761             } # if match blockdelimiter and item
762              
763             } # end foreach
764              
765             }
766              
767             # Print every line (option --match not used)
768             else {
769              
770             SWITCH: {
771 0 0 0       ( $is_add_code && $is_with_fileinfo ) and do {
  0            
772 0           print "($ARGV:$.)$state{statemarker}]$line";
773 0           last SWITCH;
774             };
775 0 0 0       ( !$is_add_code && $is_with_fileinfo ) and do {
776 0           print "($ARGV:$.)$line";
777 0           last SWITCH;
778             };
779 0 0 0       ( $is_add_code && !$is_with_fileinfo ) and do {
780 0           print "$state{statemarker}]$line";
781 0           last SWITCH;
782             };
783 0 0 0       ( !$is_add_code && !$is_with_fileinfo ) and do {
784 0           print "$line";
785 0           last SWITCH;
786             };
787             }; # switch and combine all variants
788             }
789              
790             } # end here document
791              
792 0           return;
793             }
794              
795             ### INTERNAL UTILITY ###
796             # Usage : _is_cli_with_other_than_option()
797             # Purpose : Test if @ARGV contains any other arguments than
798             # than the lonely dash '-'
799             # Returns : True (1) if found, otherwise $EMPTY_STR
800             # Throws : No
801              
802             sub _is_cli_with_other_than_option {
803              
804 0     0     my $EMPTY_STR = q{};
805 0           my @cmdlinearray = @ARGV;
806              
807             LOOP:
808 0           foreach ( 0 .. $#cmdlinearray ) {
809              
810 0 0         if ( $cmdlinearray[$_] =~ m/(-[a-zA-Z])+/ ) {
811 0           next LOOP;
812             }
813             else {
814 0           return 1; # True, found something trailing the '-'
815             }
816             }
817              
818 0           return $EMPTY_STR; # False, only '-' (or empty @ARGV)
819             }
820              
821             ### INTERNAL UTILITY ###
822             # Usage : _print_unique_delimiters()
823             # Purpose : Print only the unique delimiters.
824             # Returns : Normally returns to caller.
825             # Throws : No
826              
827             sub _print_unique_delimiters {
828 0     0     my $EMPTY_STR = q{};
829 0           my $file = shift;
830 0           my %seen;
831              
832             # Test for option --quiet (i.e. $file is set to $EMPTY_STR)
833 0 0         if ( $file ne $EMPTY_STR ) {
834 0           print "($file)";
835             }
836              
837             # Make unique delimiter list from global array of found delimiters
838 0           my @unique = grep { !$seen{$_}++ } @gl_to_be_unique_delimiters;
  0            
839              
840             # print the unique list
841 0           foreach my $item (@unique) {
842 0           print "$item ";
843             }
844              
845 0           @gl_to_be_unique_delimiters = ();
846              
847 0           return;
848             }
849              
850             ### INTERNAL UTILITY ###
851             # Usage : _write_error_file( $@, $err_filename )
852             # Purpose : Writes the error message to file in user home directory.
853             # Returns : True if file open ok, false otherwise.
854             # Throws : No
855              
856             sub _write_error_file {
857 0     0     my ( $err_str, $log_fname ) = @_;
858 0           my $EMPTY_STR = q{};
859 0           my $log_fh;
860              
861 0 0         open $log_fh, '>>', $log_fname
862             or return $EMPTY_STR;
863              
864 0           print $log_fh "$err_str\n";
865 0           close($log_fh);
866 0           return 1;
867             }
868              
869             ### INTERNAL UTILITY ###
870             # Usage : _get_error_fname()
871             # Purpose : Creates a filename for writing in user home
872             # directory with ISO8601 formated date-time stamp.
873             # Returns : The name of the error file.
874             # Throws : No
875              
876             sub _get_error_fname {
877 0     0     my $err_fname = sprintf '%s-%s.error', "$ENV{HOME}/$SCRIPT",
878             POSIX::strftime( q!%Y-%m-%d-%H.%M.%SZ!, gmtime );
879 0           return $err_fname;
880             }
881              
882             =head1 SYNOPSIS
883              
884             use 5.010;
885             use Filter::Heredoc::App qw( run_filter_heredoc );
886             run_filter_heredoc();
887            
888             =head1 DESCRIPTION
889              
890             This module implements the logic and functions to search and filter
891             here documents in scripts. Support for shell script is more mature than
892             other near compatible languages like Perl. Don't rely on current
893             version code for Perl since it's still in an early development.
894              
895             =head1 SUBROUTINES
896              
897             I exports following subroutine only on request.
898              
899             run_filter_heredoc # runs the filter-heredoc application code
900            
901             =head2 B
902              
903             run_filter_heredoc();
904            
905             This function is called by I and implements the
906             logic and functions to search and filter here documents from the
907             command line.
908              
909             =head1 ERRORS
910              
911             On user errors dies with exit(1). Exceptions for C are
912             trapped and after writing an error file, dies with exit code 2.
913            
914             =head1 BUGS AND LIMITATIONS
915              
916             I understands here documents syntax in *nix
917             shells scripts. Running other script languages will result in an
918             unpredictable output. This is not regarded as a bug.
919              
920             Please report any bugs or feature requests to
921             L or at
922             C<< >>.
923              
924             =head1 AUTHOR
925              
926             Bertil Kronlund, C<< >>
927              
928             =head1 SEE ALSO
929              
930             L, L
931              
932             =head1 LICENSE AND COPYRIGHT
933              
934             Copyright 2011-18, Bertil Kronlund
935              
936             This program is free software; you can redistribute it and/or modify it
937             under the terms of either: the GNU General Public License as published
938             by the Free Software Foundation; or the Artistic License.
939              
940             See http://dev.perl.org/licenses/ for more information.
941              
942             =cut
943              
944             1; # End of Filter::Heredoc::App