File Coverage

blib/lib/File/BOM/Utils.pm
Criterion Covered Total %
statement 40 85 47.0
branch 7 28 25.0
condition 0 9 0.0
subroutine 9 14 64.2
pod 7 7 100.0
total 63 143 44.0


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