File Coverage

blib/lib/File/BOM/Utils.pm
Criterion Covered Total %
statement 40 88 45.4
branch 7 30 23.3
condition 0 9 0.0
subroutine 9 14 64.2
pod 7 7 100.0
total 63 148 42.5


line stmt bran cond sub pod time code
1             package File::BOM::Utils;
2              
3 1     1   3537 use strict;
  1         2  
  1         25  
4 1     1   5 use warnings;
  1         2  
  1         29  
5 1     1   5 use warnings qw(FATAL utf8); # Fatalize encoding glitches.
  1         5  
  1         36  
6              
7 1     1   834 use File::Slurper qw/read_binary write_binary/;
  1         2576  
  1         70  
8              
9 1     1   3917 use Moo;
  1         27909  
  1         8  
10              
11 1     1   3428 use Types::Standard qw/Int ScalarRef Str/;
  1         78726  
  1         223  
12              
13             has action =>
14             (
15             default => sub{return ''},
16             is => 'rw',
17             isa => Str,
18             required => 1,
19             );
20              
21             has bom_name =>
22             (
23             default => sub{return ''},
24             is => 'rw',
25             isa => Str,
26             required => 0,
27             );
28              
29             has data =>
30             (
31             default => sub{return \''}, #Use ' in comment for UltraEdit syntax hiliter.
32             is => 'rw',
33             isa => ScalarRef[Str],
34             required => 0,
35             );
36              
37             has input_file =>
38             (
39             default => sub{return ''},
40             is => 'rw',
41             isa => Str,
42             required => 1,
43             );
44              
45             has output_file =>
46             (
47             default => sub{return ''},
48             is => 'rw',
49             isa => Str,
50             required => 1,
51             );
52              
53             # http://search.cpan.org/perldoc?PPI::Token::BOM or String::BOM.
54              
55             our(%bom2name) =
56             (
57             "\x00\x00\xfe\xff" => 'UTF-32-BE',
58             "\xff\xfe\x00\x00" => 'UTF-32-LE',
59             "\xfe\xff" => 'UTF-16-BE',
60             "\xff\xfe" => 'UTF-16-LE',
61             "\xef\xbb\xbf" => 'UTF-8',
62             );
63              
64             our(%name2bom) =
65             (
66             'UTF-32-BE' => "\x00\x00\xfe\xff",
67             'UTF-32-LE' => "\xff\xfe\x00\x00",
68             'UTF-16-BE' => "\xfe\xff",
69             'UTF-16-LE' => "\xff\xfe",
70             'UTF-8' => "\xef\xbb\xbf",
71             );
72              
73             our $VERSION = '1.02';
74              
75             # ------------------------------------------------
76              
77             sub add
78             {
79 0     0 1 0 my($self, %opt) = @_;
80              
81 0         0 $self -> _read(%opt);
82 0 0       0 $self -> bom_name($opt{bom_name}) if (defined $opt{bom_name});
83 0 0       0 $self -> output_file($opt{output_file}) if (defined $opt{input_file});
84              
85 0         0 my($output_file) = $self -> output_file;
86 0         0 my($name) = $self -> bom_name;
87              
88 0 0       0 die "Unknown BOM name: $name\n" if (! $name2bom{$name});
89              
90 0         0 write_binary($output_file, $name2bom{$name} . ${$self -> data});
  0         0  
91              
92             # Return 0 for success and 1 for failure.
93              
94 0         0 return 0;
95              
96             } # End of add.
97              
98             # ------------------------------------------------
99              
100             sub bom_report
101             {
102 0     0 1 0 my($self, %opt) = @_;
103              
104 0 0       0 $self -> bom_name($opt{bom_name}) if (defined $opt{bom_name});
105              
106 0         0 my($name) = $self -> bom_name;
107              
108             return
109             {
110             length => length($name2bom{$name}) || 0,
111             name => $name,
112 0   0     0 value => $name2bom{$name} || 0,
      0        
113             };
114              
115             } # End of bom_report.
116              
117             # ------------------------------------------------
118              
119             sub bom_values
120             {
121 6     6 1 8 my($self) = @_;
122              
123 6         24 return sort{4 - length($a) <=> 4 - length($b)} keys %bom2name;
  42         64  
124              
125             } # End of bom_values;
126              
127             # ------------------------------------------------
128              
129             sub file_report
130             {
131 6     6 1 7154 my($self, %opt) = @_;
132              
133 6         17 $self -> _read(%opt);
134              
135 6         62 my($data) = ${$self -> data};
  6         112  
136 6         32 my($name) = ''; # Sugar: Make $name not null.
137 6         10 my($value) = 0; # Sugar: Make $value not null.
138              
139 6         7 my($length);
140              
141             # Sort from long to short to avoid false positives.
142              
143 6         17 for my $key ($self -> bom_values)
144             {
145 20         18 $length = length $key;
146              
147             # Warning: Use eq and not ==.
148              
149 20 100       48 if (substr($data, 0, $length) eq $key)
150             {
151 5         7 $value = $key;
152 5         8 $name = $bom2name{$key};
153 5         10 substr($data, 0, $length) = '';
154              
155 5         7 last;
156             }
157             }
158              
159             return
160             {
161 6 100       48 length => $name ? $length : 0,
    100          
162             message => $name ? "BOM name $name found" : 'No BOM found',
163             name => $name,
164             value => $value,
165             };
166              
167 0         0 return 0;
168              
169             } # End of file_report.
170              
171             # ------------------------------------------------
172              
173             sub _read
174             {
175 6     6   9 my($self, %opt) = @_;
176              
177 6 50       17 $self -> input_file($opt{input_file}) if (defined $opt{input_file});
178 6         114 $self -> data(\read_binary($self -> input_file) );
179              
180             # Return 0 for success and 1 for failure.
181              
182 6         1125 return 0;
183              
184             } # End of _read.
185              
186             # ------------------------------------------------
187              
188             sub remove
189             {
190 0     0 1   my($self, %opt) = @_;
191 0           my($result) = $self -> file_report(%opt);
192              
193 0 0         $self -> output_file($opt{output_file}) if (defined $opt{input_file});
194              
195 0 0         die "Output file not specified\n" if (length($self -> output_file) == 0);
196              
197 0           my($output_file) = $self -> output_file;
198              
199 0           substr(${$self -> data}, 0, $$result{length}) = '';
  0            
200              
201 0           write_binary($output_file, ${$self -> data});
  0            
202              
203             # Return 0 for success and 1 for failure.
204              
205 0           return 0;
206              
207             } # End of remove.
208              
209             # ------------------------------------------------
210              
211             sub run
212             {
213 0     0 1   my($self, %opt) = @_;
214 0   0       my($action) = lc($opt{action} || $self -> action || '');
215 0           my(%sugar) =
216             (
217             a => 'add',
218             r => 'remove',
219             t => 'test',
220             );
221 0   0       $action = $sugar{$action} || $action;
222 0           my(%action) =
223             (
224             add => 1,
225             remove => 1,
226             test => 1,
227             );
228              
229 0 0         $self -> input_file($opt{input_file}) if (defined $opt{input_file});
230              
231 0 0         die "Input file not specified\n" if (length($self -> input_file) == 0);
232 0 0         die "Unknown action '$action'\n" if (! $action{$action});
233              
234 0           $self -> $action(%opt);
235              
236             # Return 0 for success and 1 for failure.
237              
238 0           return 0;
239              
240             } # End of run.
241              
242             # ------------------------------------------------
243              
244             sub test
245             {
246 0     0 1   my($self, %opt) = @_;
247 0           my($result) = $self -> file_report(%opt);
248 0           my($file_name) = $self -> input_file;
249              
250 0           print "BOM report for $file_name: \n";
251 0           print 'File size: ', -s $file_name, " bytes \n";
252              
253 0           my($prefix);
254              
255 0           for my $key (qw/message name/)
256             {
257 0 0         $prefix = ($key eq 'message') ? 'Message' : 'BOM name';
258              
259 0           print "$prefix: $$result{$key}\n";
260             }
261              
262 0 0         if ($$result{name})
263             {
264 0           my($stats) = $self -> bom_report(bom_name => $$result{name});
265              
266 0           print "Length: $$stats{length} bytes \n";
267             }
268              
269             # Return 0 for success and 1 for failure.
270              
271 0           return 0;
272              
273             } # End of test.
274              
275             # ------------------------------------------------
276              
277             1;
278              
279             =pod
280              
281             =head1 NAME
282              
283             C - Check, Add and Remove BOMs
284              
285             =head1 Synopsis
286              
287             This is scripts/synopsis.pl:
288              
289             #!/usr/bin/env perl
290              
291             use strict;
292             use warnings;
293              
294             use File::BOM::Utils;
295             use File::Spec;
296              
297             # -------------------
298              
299             my($bommer) = File::BOM::Utils -> new;
300             my($file_name) = File::Spec -> catfile('data', 'bom-UTF-8.xml');
301              
302             $bommer -> action('test');
303             $bommer -> input_file($file_name);
304              
305             my($report) = $bommer -> file_report;
306              
307             print "BOM report for $file_name: \n";
308             print join("\n", map{"$_: $$report{$_}"} sort keys %$report), "\n";
309              
310             Try 'bommer.pl -h'. It is installed automatically when the module is installed.
311              
312             =head1 Description
313              
314             L provides a means of testing, adding and removing BOMs (Byte-Order-Marks)
315             within files.
316              
317             It also provides two hashes accessible from outside the module, which convert in both directions
318             between BOM names and values. These hashes are called C<%bom2name> and C<%name2bom>.
319              
320             See also bommer.pl, which is installed automatically when the module is installed.
321              
322             =head1 Distributions
323              
324             This module is available as a Unix-style distro (*.tgz).
325              
326             See L
327             for help on unpacking and installing distros.
328              
329             =head1 Installation
330              
331             Install L as you would any C module:
332              
333             Run:
334              
335             cpanm File::BOM::Utils
336              
337             or run:
338              
339             sudo cpan File::BOM::Utils
340              
341             or unpack the distro, and then either:
342              
343             perl Build.PL
344             ./Build
345             ./Build test
346             sudo ./Build install
347              
348             or:
349              
350             perl Makefile.PL
351             make (or dmake or nmake)
352             make test
353             make install
354              
355             =head1 Constructor and Initialization
356              
357             C is called as C<< my($parser) = File::BOM::Utils -> new(k1 => v1, k2 => v2, ...) >>.
358              
359             It returns a new object of type C.
360              
361             Key-value pairs accepted in the parameter list (see corresponding methods for details
362             [e.g. L]):
363              
364             =over 4
365              
366             =item o action => $string
367              
368             Specify the action wanted:
369              
370             =over 4
371              
372             =item o add
373              
374             Add the BOM named with the C option to C.
375             Write the result to C.
376              
377             =item o remove
378              
379             Remove any BOM found from the C. Write the result to C.
380              
381             The output is created even if the input file has no BOM, in order to not violate the
382             L.
383              
384             =item o test
385              
386             Print the BOM status of C.
387              
388             The methods L and L return hashrefs if you wish to
389             avoid printed output.
390              
391             =back
392              
393             Default: ''.
394              
395             A value for this option is mandatory.
396              
397             Note: As syntactic sugar, you may specify just the 1st letter of the action. And that's why
398             C is called C and not C.
399              
400             =item o bom_name => $string
401              
402             Specify which BOM to add to C.
403              
404             This option is mandatory if the C is C.
405              
406             Values (always upper-case):
407              
408             =over 4
409              
410             =item o UTF-32-BE
411              
412             =item o UTF-32-LE
413              
414             =item o UTF-16-BE
415              
416             =item o UTF-16-LE
417              
418             =item o UTF-8
419              
420             =back
421              
422             Default: ''.
423              
424             Note: These names are taken from the test data for L.
425              
426             =item o input_file => $string
427              
428             Specify the name of the input file. It is read in C<:raw> mode.
429              
430             A value for this option is mandatory.
431              
432             Default: ''.
433              
434             =item o output_file => $string
435              
436             Specify the name of the output file for when the action is C or C.
437             It is written in C<:raw> mode.
438              
439             And yes, it can be the same as the input file, but does not default to the input file.
440             That would be dangerous.
441              
442             This option is mandatory if the C is C or C.
443              
444             Default: ''.
445              
446             =back
447              
448             =head1 Methods
449              
450             =head2 action([$string])
451              
452             Here, the [] indicate an optional parameter.
453              
454             Gets or sets the action name, as a string.
455              
456             If you supplied an abbreviated (1st letter only) version of the action, the return value is the
457             full name of the action.
458              
459             C is a parameter to L.
460              
461             =head2 add([%opt])
462              
463             Here, the [] indicate an optional parameter.
464              
465             Adds a named BOM to the input file, and writes the result to the output file.
466              
467             Returns 0.
468              
469             C<%opt> may contain these (key => value) pairs:
470              
471             =over 4
472              
473             =item o bom_name => $string
474              
475             The name of the BOM.
476              
477             The names are listed above, under L.
478              
479             =item o input_file => $string
480              
481             =item o output_file => $string
482              
483             =back
484              
485             =head2 bom_name([$string])
486              
487             Here, the [] indicate an optional parameter.
488              
489             Gets or sets the name of the BOM to add to the input file as that file is copied to the output file.
490              
491             The names are listed above, under L.
492              
493             C is a parameter to L.
494              
495             =head2 bom_report([%opt])
496              
497             Here, the [] indicate an optional parameter.
498              
499             Returns a hashref of statitics about the named BOM.
500              
501             C<%opt> may contain these (key => value) pairs:
502              
503             =over 4
504              
505             =item o bom_name => $string
506              
507             =back
508              
509             The hashref returned has these (key => value) pairs:
510              
511             =over 4
512              
513             =item o length => $integer
514              
515             The # of bytes in the BOM.
516              
517             =item o name => $string
518              
519             The name of the BOM.
520              
521             The names are listed above, under L.
522              
523             =item o value => $integer
524              
525             The value of the named BOM.
526              
527             =back
528              
529             =head2 bom_values()
530              
531             Returns an array of BOM values, sorted from longest to shortest.
532              
533             =head2 data()
534              
535             Returns a reference to a string holding the contents input file, or returns a reference to the
536             empty string.
537              
538             =head2 file_report([%opt])
539              
540             Here, the [] indicate an optional parameter.
541              
542             Returns a hashref of statistics about the input file.
543              
544             C<%opt> may contain these (key => value) pairs:
545              
546             =over 4
547              
548             =item o input_file => $string
549              
550             =back
551              
552             The hashref returned has these (key => value) pairs:
553              
554             =over 4
555              
556             =item o length => $name ? $length : 0
557              
558             This is the length of the BOM in bytes.
559              
560             =item o message => $name ? "BOM name $name found" : 'No BOM found'
561              
562             =item o name => $name || ''
563              
564             The name of the BOM.
565              
566             The names are listed above, under L.
567              
568             =item o value => $value || 0
569              
570             This is the value of the BOM.
571              
572             =back
573              
574             =head2 input_file([$string])
575              
576             Here, the [] indicate an optional parameter.
577              
578             Gets or sets the name of the input file.
579              
580             C is a parameter to L.
581              
582             =head2 new([%opt])
583              
584             Here, the [] indicate an optional parameter.
585              
586             Returns an object of type C.
587              
588             C<%opt> may contain these (key => value) pairs:
589              
590             =over 4
591              
592             =item o action => $string
593              
594             The action wanted.
595              
596             The actions are listed above, under L.
597              
598             =item o bom_name => $string
599              
600             The name of the BOM.
601              
602             The names are listed above, under L.
603              
604             =item o input_file => $string
605              
606             =item o output_file => $string
607              
608             =back
609              
610             =head2 output_file([$string])
611              
612             Here, the [] indicate an optional parameter.
613              
614             Gets or sets the name of the output file.
615              
616             And yes, it can be the same as the input file, but does not default to the input file.
617             That would be dangerous.
618              
619             C is a parameter to L.
620              
621             =head2 remove(%opt)
622              
623             Here, the [] indicate an optional parameter.
624              
625             Removes any BOM from the input file, and writes the result to the output_file.
626              
627             C<%opt> may contain these (key => value) pairs:
628              
629             =over 4
630              
631             =item o input_file => $string
632              
633             =item o output_file => $string
634              
635             =back
636              
637             =head2 run(%opt)
638              
639             Here, the [] indicate an optional parameter.
640              
641             This is the only method users would normally call, but you can call directly any of the
642             3 methods mentioned next.
643              
644             C<%opt> is passed to L, L and L.
645              
646             Returns 0.
647              
648             C<%opt> may contain these (key => value) pairs:
649              
650             =over 4
651              
652             =item o action => $string
653              
654             The action wanted.
655              
656             The actions are listed above, under L.
657              
658             =item o bom_name => $string
659              
660             The name of the BOM.
661              
662             The names are listed above, under L.
663              
664             =item o input_file => $string
665              
666             =item o output_file => $string
667              
668             =back
669              
670             =head1 test([%opt])
671              
672             Here, the [] indicate an optional parameter.
673              
674             Print to STDOUT various statistics pertaining to the input file.
675              
676             C<%opt> may contain these (key => value) pairs:
677              
678             =over 4
679              
680             =item o input_file => $string
681              
682             =back
683              
684             =head1 FAQ
685              
686             =head2 How does this module read and write files?
687              
688             It uses L's read_binary() and write_binary().
689              
690             =head2 What are the hashes accessible from outside the module?
691              
692             They are called C<%bom2name> and C<%name2bom>.
693              
694             The BOM names used are listed under L.
695              
696             =head2 Which program is installed when the module is installed?
697              
698             It is called C. Run it with the -h option, to display help.
699              
700             =head2 How is the parameter %opt, which may be passed to many methods, handled?
701              
702             The keys in C<%opt> are used to find values which are passed to the methods named after the
703             keys.
704              
705             For instance, if you call:
706              
707             my($bommer) = File::BOM::Utils -> new(action => 'add');
708              
709             $bommer -> run(action => 'test');
710              
711             Then the code calls C, which sets the 'current' value of C to C.
712              
713             This means that if you later call C, the value returned is whatever was the most recent
714             value provided (to any method) in C<$opt{action}>. Similarly for the other parameters to
715             L.
716              
717             Note: As syntactic sugar, you may specify just the 1st letter of the action. And that's why
718             C is called C and not C.
719              
720             =head2 What happens if I add the same BOM twice?
721              
722             The program will do as you order it to do. Hopefully, you remove one or both of the BOMs immediately
723             after testing the output.
724              
725             =head1 See Also
726              
727             L.
728              
729             L.
730              
731             L.
732              
733             L, whose test data I've adopted.
734              
735             L.
736              
737             =head1 Machine-Readable Change Log
738              
739             The file Changes was converted into Changelog.ini by L.
740              
741             =head1 Version Numbers
742              
743             Version numbers < 1.00 represent development versions. From 1.00 up, they are production versions.
744              
745             =head1 Repository
746              
747             L
748              
749             =head1 Support
750              
751             Email the author, or log a bug on RT:
752              
753             L.
754              
755             =head1 Author
756              
757             L was written by Ron Savage Iron@savage.net.auE> in 2015.
758              
759             Marpa's homepage: L.
760              
761             My homepage: L.
762              
763             =head1 Copyright
764              
765             Australian copyright (c) 2015, Ron Savage.
766              
767             All Programs of mine are 'OSI Certified Open Source Software';
768             you can redistribute them and/or modify them under the terms of
769             The Artistic License 2.0, a copy of which is available at:
770             http://opensource.org/licenses/alphabetical.
771              
772             =cut