File Coverage

blib/lib/C/Utility.pm
Criterion Covered Total %
statement 43 166 25.9
branch 3 56 5.3
condition 0 15 0.0
subroutine 12 30 40.0
pod 20 22 90.9
total 78 289 26.9


line stmt bran cond sub pod time code
1             package C::Utility;
2 2     2   140407 use warnings;
  2         12  
  2         68  
3 2     2   11 use strict;
  2         3  
  2         38  
4 2     2   10 use File::Spec;
  2         3  
  2         48  
5 2     2   10 use Carp;
  2         4  
  2         148  
6 2     2   1003 use File::Versions 'make_backup';
  2         2459  
  2         120  
7 2     2   913 use File::Slurper qw/read_text write_text/;
  2         28625  
  2         148  
8 2     2   975 use C::Tokenize qw/$comment_re $include $reserved_re/;
  2         8894  
  2         340  
9 2     2   957 use Text::LineNumber;
  2         709  
  2         4581  
10              
11             require Exporter;
12              
13             our @ISA = qw(Exporter);
14              
15             our @EXPORT_OK = qw/
16             add_lines
17             brute_force_line
18             c_string
19             c_to_h_name
20             ch_files
21             convert_to_c_string
22             convert_to_c_string_pc
23             escape_string
24             hash_to_c_file
25             line_directive
26             linein
27             lineout
28             print_bottom_h_wrapper
29             print_top_h_wrapper
30             read_includes
31             remove_quotes
32             stamp_file
33             valid_c_variable
34             /;
35              
36             our %EXPORT_TAGS = (
37             'all' => \@EXPORT_OK,
38             );
39              
40             our $VERSION = '0.011';
41              
42             sub convert_to_c_string
43             {
44 2     2 1 88 my ($text) = @_;
45 2 50       9 if (length ($text) == 0) {
46 0         0 return "\"\"";
47             }
48             # Convert backslashes to double backslashes.
49 2         5 $text =~ s/\\/\\\\/g;
50             # Escape double quotes
51 2         6 $text = escape_string ($text);
52             # If there was a backslash before a quote, as in \", the first
53             # regex above converted it to \\", and then escape_string
54             # converted that to \\\".
55 2         5 $text =~ s/\\\\"/\\"/g;
56             # Remove backslashes from before the @ symbol.
57 2         4 $text =~ s/\\\@/@/g;
58             # Turn each line into a string
59 2         11 $text =~ s/(.*)\n/"$1\\n"\n/gm;
60             # Catch a final line without any \n at its end.
61 2 100       8 if ($text !~ /\\n\"$/) {
62 1         10 $text =~ s/(.+)$/"$1"/g;
63             }
64 2         6 return $text;
65             }
66              
67             sub c_string
68             {
69 0     0 1 0 goto & convert_to_c_string;
70             }
71              
72             sub ch_files
73             {
74 0     0 1 0 my ($c_file_name) = @_;
75 0 0       0 if ($c_file_name !~ /\.c/) {
76 0         0 die "$c_file_name is not a C file name";
77             }
78 0         0 my $h_file_name = $c_file_name;
79 0         0 $h_file_name =~ s/\.c$/\.h/;
80 0 0       0 if (-f $c_file_name) {
81 0         0 make_backup ($c_file_name);
82             }
83 0 0       0 if (-f $h_file_name) {
84 0         0 make_backup ($h_file_name);
85             }
86 0         0 return $h_file_name;
87             }
88              
89             sub convert_to_c_string_pc
90             {
91 1     1 1 456 my ($text) = @_;
92 1         6 $text =~ s/%/%%/g;
93 1         3 return convert_to_c_string ($text);
94             }
95              
96             sub escape_string
97             {
98 2     2 1 4 my ($text) = @_;
99 2         5 $text =~ s/\"/\\\"/g;
100 2         5 return $text;
101             }
102              
103             sub c_to_h_name
104             {
105 0     0 1 0 my ($c_file_name) = @_;
106 0 0       0 if ($c_file_name !~ /\.c/) {
107 0         0 die "$c_file_name is not a C file name";
108             }
109 0         0 my $h_file_name = $c_file_name;
110 0         0 $h_file_name =~ s/\.c$/\.h/;
111 0         0 return $h_file_name;
112             }
113              
114             sub valid_c_variable
115             {
116 0     0 1 0 my ($variable_name) = @_;
117 0 0 0     0 if ($variable_name !~ /^[A-Za-z_][A-Za-z_0-9]+$/ ||
118             $variable_name =~ /^(?:$reserved_re)$/) {
119 0         0 return;
120             }
121 0         0 return 1;
122             }
123              
124             # Wrapper name
125             # BKB 2009-10-05 14:09:41
126              
127             sub wrapper_name
128             {
129 0     0 1 0 my ($string) = @_;
130 0         0 $string =~ s/[.-]/_/g;
131 0 0       0 if (! valid_c_variable ($string)) {
132 0         0 croak "Bad string for wrapper '$string'";
133             }
134 0         0 my $wrapper_name = uc $string;
135 0         0 return $wrapper_name;
136             }
137              
138             sub print_top_h_wrapper
139             {
140 0     0 1 0 my ($fh, $file_name) = @_;
141            
142 0         0 my $wrapper_name = wrapper_name ($file_name);
143 0         0 my $wrapper = <
144             #ifndef $wrapper_name
145             #define $wrapper_name
146             EOF
147 0         0 print_out ($fh, $wrapper);
148             }
149              
150             sub print_out
151             {
152 0     0 0 0 my ($fh, $wrapper) = @_;
153 0 0 0     0 if (ref $fh && ref $fh eq 'SCALAR') {
154 0         0 ${$fh} .= $wrapper;
  0         0  
155             }
156             else {
157 0         0 print $fh $wrapper;
158             }
159             }
160              
161             sub print_bottom_h_wrapper
162             {
163 0     0 1 0 my ($fh, $file_name) = @_;
164 0         0 my $wrapper_name = wrapper_name ($file_name);
165 0         0 my $wrapper = <
166             #endif /* $wrapper_name */
167             EOF
168 0         0 print_out ($fh, $wrapper);
169             }
170              
171             sub print_include
172             {
173 0     0 1 0 my ($fh, $h_file_name) = @_;
174 0         0 print $fh <
175             #include "$h_file_name"
176             EOF
177             }
178              
179             sub hash_to_c_file
180             {
181             # $prefix is an optional prefix applied to all variables.
182 0     0 1 0 my ($c_file_name, $hash_ref, $prefix) = @_;
183 0         0 my $h_file_name = ch_files ($c_file_name);
184 0 0       0 die "Not a hash ref" unless ref $hash_ref eq "HASH";
185 0 0       0 $prefix = "" unless $prefix;
186 0 0       0 open my $c_out, ">:utf8", $c_file_name or die $!;
187 0         0 my (undef, undef, $h_file) = File::Spec->splitpath ($h_file_name);
188 0         0 print_include ($c_out, $h_file);
189 0 0       0 open my $h_out, ">:utf8", $h_file_name or die $!;
190 0         0 print_top_h_wrapper ($h_out, $h_file);
191 0         0 for my $variable (sort keys %$hash_ref) {
192 0 0       0 if (! valid_c_variable ($variable)) {
193 0         0 croak "key '$variable' is not a valid C variable";
194             }
195 0         0 my $value = $hash_ref->{$variable};
196 0         0 $value = convert_to_c_string ($value);
197 0         0 print $c_out "const char * $prefix$variable = $value;\n";
198 0         0 print $h_out "extern const char * $prefix$variable; /* $value */\n";
199             }
200 0 0       0 close $c_out or die $!;
201 0         0 print_bottom_h_wrapper ($h_out, $h_file);
202 0 0       0 close $h_out or die $!;
203 0         0 return $h_file_name;
204             }
205              
206             sub line_directive
207             {
208 0     0 1 0 my ($output, $line_number, $file_name) = @_;
209 0 0 0     0 die "$line_number is not a positive integer number"
210             unless $line_number =~ /^[0-9]+$/ && $line_number > 0;
211 0         0 print_out ($output, "#line $line_number \"$file_name\"\n");
212             }
213              
214             sub brute_force_line
215             {
216 0     0 1 0 my ($input_file, $output_file) = @_;
217 0 0       0 open my $input, "<:encoding(utf8)", $input_file or die $!;
218 0 0       0 open my $output, ">:encoding(utf8)", $output_file or die $!;
219 0         0 while (<$input>) {
220 0         0 print $output "#line $. \"$input_file\"\n";
221 0         0 print $output $_;
222             }
223 0 0       0 close $input or die $!;
224 0 0       0 close $output or die $!;
225             }
226              
227             sub add_lines
228             {
229 0     0 1 0 my ($input_file) = @_;
230 0         0 my $full_name = File::Spec->rel2abs ($input_file);
231 0         0 my $text = '';
232 0 0       0 open my $input, "<:encoding(utf8)", $input_file or die $!;
233 0         0 while (<$input>) {
234 0 0       0 if (/^#line/) {
    0          
235 0         0 my $line_no = $. + 1;
236 0         0 $text .= "#line $line_no \"$full_name\"\n";
237             }
238             elsif ($. == 1) {
239 0         0 $text .= "#line 1 \"$full_name\"\n";
240 0         0 $text .= $_;
241             }
242             else {
243 0         0 $text .= $_;
244             }
245             }
246 0         0 return $text;
247             }
248              
249             sub remove_quotes
250             {
251 3     3 1 93 my ($string) = @_;
252 3         25 $string =~ s/^"|"$|"\s*"//g;
253 3         15 return $string;
254             }
255             #use Data::Dumper;
256             sub linedirective
257             {
258 0     0 0   my ($intext, $file, $directive) = @_;
259 0 0 0       die unless $intext && $file && $directive;
      0        
260             # This module is pretty reliable for line numbering.
261 0           my $tln = Text::LineNumber->new ($intext);
262 0           my %renumbered;
263             # Uniquifier for the lines.
264 0           my $count = 0;
265             # This is unlikely to occur.
266 0           my $tag = 'ABRACADABRA';
267             # Watch for blue-moon occurences
268 0 0         die if $intext =~ /$tag\d+/;
269 0           while ($intext =~ s/^\Q$directive/$tag$count$tag/sm) {
270 0           $count++;
271             }
272             # "pos" doesn't work well with s///g, so now we need to match the tags
273             # one by one.
274 0           while ($intext =~ /($tag\d+$tag)/g) {
275 0           my $key = $1;
276 0           my $pos = pos ($intext);
277 0           my $line = $tln->off2lnr ($pos);
278             # print "Position $pos in $file = line $line.\n";
279 0           $renumbered{$key} = $line;
280             }
281             #print Dumper (\%renumbered);
282 0           $intext =~ s/($tag\d+$tag)/#line $renumbered{$1} "$file"/g;
283             # Check for failures. We already checked this doesn't occur
284             # naturally in the file above.
285 0 0         die if $intext =~ /$tag\d+$tag/;
286 0           return $intext;
287             }
288              
289             sub linein
290             {
291 0     0 1   my ($infile) = @_;
292 0           my $intext = read_text ($infile);
293 0           $intext = linedirective ($intext, $infile, '#linein');
294 0           return $intext;
295             }
296              
297             sub lineout
298             {
299 0     0 1   my ($outtext, $outfile) = @_;
300              
301 0           $outtext = linedirective ($outtext, $outfile, "#lineout");
302 0           write_text ($outfile, $outtext);
303             }
304              
305             sub stamp_file
306             {
307 0     0 1   my ($fh, $name) = @_;
308 0 0         if (! defined $name) {
309 0           $name = "This C file";
310             }
311 0           my $now = scalar localtime ();
312 0           my $stamp =<
313             /*
314             $name was generated by $0 at $now.
315             */
316             EOF
317 0           print_out ($fh, $stamp);
318             }
319              
320             sub read_includes
321             {
322 0     0 1   my ($file) = @_;
323 0           my $text = read_text ($file);
324             # Remove all the comments from the file so that things like
325             # /*#include "something.h"*/ don't create false positives.
326 0           $text =~ s/$comment_re//g;
327 0           my @hfiles;
328 0           while ($text =~ /$include/g) {
329 0           push @hfiles, $1;
330             }
331 0           return \@hfiles;
332             }
333              
334             1;