File Coverage

bin/cpppp
Criterion Covered Total %
statement 63 112 56.2
branch 15 42 35.7
condition 3 18 16.6
subroutine 12 22 54.5
pod n/a
total 93 194 47.9


line stmt bran cond sub pod time code
1             #! /usr/bin/env perl
2             # PODNAME: cpppp
3 8         1530036 our $VERSION = '0.005'; # VERSION
4             # ABSTRACT: Command line tool to process cpppp templates
5 8     8   56476 use v5.20;
  8         32  
6 8     8   45 use warnings;
  8         16  
  8         571  
7 8     8   4310 use FindBin;
  8         17248  
  8         601  
8 8     8   4964 use experimental 'signatures';
  8         50974  
  8         61  
9 8     8   8519 use CodeGen::Cpppp;
  8         30  
  8         507  
10 8     8   8789 use autouse 'Pod::Usage' => 'pod2usage';
  8         9188  
  8         52  
11 8     8   7551 use Getopt::Long;
  8         122438  
  8         43  
12              
13              
14 8         42 our @original_argv= @ARGV;
15 8         22 our @script;
16 8         27 my %param;
17             our %option_spec= (
18 0     0   0 'param|p=s' => sub { set_param(split /=/, $_[1], 2) },
19 0     0   0 'features=s' => sub { set_feature($_) for split ',', $_[1] },
20             'dump-pl' => \my $opt_dump_pl,
21             'list-sections' => \my $opt_list_sections,
22             'convert-linecomment-to-c89' => \my $convert_linecomment_to_c89,
23 0     0   0 'call=s' => sub { push @script, [ 'call_method', $_[1] ] },
24 0     0   0 'eval=s' => sub { push @script, [ 'do_eval', $_[1] ] },
25 8     8   10892 'out|section-out|o=s' => sub { push @script, [ 'output', $_[1] ] },
26 2     2   1709 '<>' => sub { push @script, [ 'process_tpl', $_[0] ] },
27 0     0   0 'help' => sub { pod2usage(1) },
28 0     0   0 'version' => sub { say CodeGen::Cpppp->VERSION; exit 0 },
  0         0  
29 8         304 );
30 8 50       75 GetOptions(%option_spec) or pod2usage(2);
31              
32             # If no 'process_tpl' item exists, create one from STDIN.
33 8 100       311 unless (grep $_->[0] eq 'process_tpl', @script) {
34 6         27 unshift @script, [ 'process_tpl', \*STDIN, 'stdin' ];
35             # warn unsuspecting users
36 6 50 33     79 STDERR->print("(reading template from stdin)\n")
37             if -t STDIN && -t STDERR;
38             }
39             # 'process_tpl' needs to happen before any other action in the @script
40             # but the user may have specified the file name last.
41 8 100       38 if ($script[0][0] ne 'process_tpl') {
42 1         1 my $i= 0;
43 1         5 ++$i while $script[$i][0] ne 'process_tpl';
44 1         4 unshift @script, splice(@script, $i, 1);
45             }
46             # --list-sections suppresses output
47 8 50       71 if ($opt_list_sections) {
    50          
48 0         0 @script= grep $_->[0] ne 'output', @script;
49             }
50             elsif (!grep $_->[0] eq 'output') {
51             # If there was no 'out' specified, add one to STDOUT
52 8         133 push @script, [ 'output', '-' ];
53             }
54              
55 0     0   0 sub set_param($var, $value) {
  0         0  
  0         0  
  0         0  
56 0 0       0 $var =~ /^( [\$\@\%]? ) [\w_]+ $/x
57             or die "Parameter name '$var' is not valid\n";
58 0 0       0 if ($1) {
59 0 0       0 my $expr= $1 eq '$'? '$param{$var}='.$value
    0          
60             : $1 eq '@'? '$param{$var}=['.$value.']'
61             : '$param{$var}={'.$value.'}';
62             # Automatically require modules mentioned in the expression
63 0         0 while (/\b([A-Za-z][\w_]+(::[A-Za-z0-9][\w_]+)+)\b/) {
64 0         0 my $fname= $1 . '.pm';
65 0         0 $fname =~ s,::,/,g;
66 0         0 eval { require $fname };
  0         0  
67             }
68 0 0       0 eval "use strict; use warnings; $expr; 1"
69             or die "Error evaluating parameter '$var': $@\n";
70             } else {
71 0   0     0 $param{'$'.$var} //= $value;
72 0 0 0     0 $param{'@'.$var} //= [ split ',', $value ]
73             if $value =~ /,/;
74 0         0 my ($k, $v);
75 0 0 0     0 $param{'%'.$var} //= { map +(($k,$v)=split('=',$_,2)), split ',', $value }
76             if $value =~ /=/;
77             }
78             }
79 0     0   0 sub set_feature($expr) {
  0         0  
  0         0  
80 0         0 my ($k, $v)= split '=', $expr, 2;
81 0   0     0 set_param("feature_$k", $v // 1);
82             }
83              
84 8         116 my $cpppp= CodeGen::Cpppp->new(
85             convert_linecomment_to_c89 => $convert_linecomment_to_c89,
86             );
87              
88 8         16 my $tpl;
89 8     8   14 sub process_tpl(@input_args) {
  8         19  
  8         14  
90 8 50       26 if ($opt_dump_pl) {
91 0         0 my $parse= $cpppp->parse_cpppp(@input_args);
92 0         0 my $code= $cpppp->_gen_perl_template_package($parse, with_data => 1);
93 0   0     0 my $sec= $input_args[1] // $input_args[0];
94 0         0 $cpppp->output->declare_sections($sec);
95 0         0 $cpppp->output->append($sec, $code)
96             } else {
97 8         92 my $tpl_class= $cpppp->compile_cpppp(@input_args);
98 8         109 my $tpl_params= $tpl_class->coerce_parameters(\%param);
99 8         40 $tpl= $cpppp->new_template($tpl_class, $tpl_params);
100             }
101             }
102              
103 0     0   0 sub do_eval($code) {
  0         0  
  0         0  
104 0 0       0 eval $code or die "Eval '$code' failed: $@\n";
105             }
106              
107 0     0   0 sub call_method($code) {
  0         0  
  0         0  
108 0 0       0 defined $tpl or die "No template is defined, for --call";
109 0         0 do_eval("\$tpl->$code");
110             }
111              
112 15     15   31 sub output($spec) {
  15         31  
  15         24  
113 15         85 my ($filespec, $sections)= reverse split /=/, $spec, 2;
114 15 100 66     144 if ($filespec eq '-' || !length $filespec) {
115 7         39 print $cpppp->get_filtered_output($sections);
116             } else {
117 8         82 $cpppp->write_sections_to_file($sections, split('@', $filespec, 2));
118             }
119 15 100       77 $cpppp->output->consume(defined $sections? ($sections) : ());
120             }
121              
122             # All the global options are taken care of. Now execute the "script options"
123             # in the order they were given.
124 8         24 for (@script) {
125 23         91 my ($method, @args)= @$_;
126 23 50       270 $method= main->can($method) or die 'bug';
127 23         99 $method->(@args);
128             }
129              
130 7 50       0 if ($opt_list_sections) {
131 0         0 say "name\tline_count";
132 0         0 for my $s ($cpppp->output->section_list) {
133 0         0 my $line_count= ()= $cpppp->output->get($s) =~ /\n/g;
134 0         0 say "$s\t$line_count";
135             }
136 0         0 exit 0;
137             }
138              
139             # Lets a template main::re_exec(@different_args)
140 1     1   2 sub re_exec(@new_argv) {
  1         3  
  1         2  
141 1 0         exec($^X, "$FindBin::RealBin/$FindBin::RealScript", @new_argv)
142             or die "exec: $!";
143             }
144              
145             __END__