File Coverage

blib/lib/File/LocalizeNewlines.pm
Criterion Covered Total %
statement 86 93 92.4
branch 31 52 59.6
condition 11 27 40.7
subroutine 21 21 100.0
pod 6 6 100.0
total 155 199 77.8


line stmt bran cond sub pod time code
1             package File::LocalizeNewlines;
2              
3             =pod
4              
5             =head1 NAME
6              
7             File::LocalizeNewlines - Localize the newlines for one or more files
8              
9             =head1 DESCRIPTION
10              
11             For people that routinely work with a mixture of different platforms
12             that have conflicting newline formats (mainly *NIX and Win32) there
13             are a number of different situations that can result in files having
14             their newlines get corrupted.
15              
16             File::LocalizeNewlines provides a mechanism for one off or bulk
17             detection and conversion of these files to the newline style for the
18             local platform.
19              
20             The module implements the conversion using a standard "universal line
21             seperator" regex, which ensures that files with any of the different
22             newlines, plus a couple of common "broken" newlines, including
23             multiple different types mixed in the same file, are all converted to
24             the local platform's newline style.
25              
26             =head1 METHODS
27              
28             =cut
29              
30 2     2   120498 use 5.005;
  2         8  
  2         85  
31 2     2   11 use strict;
  2         6  
  2         107  
32 2     2   13 use File::Spec 0.80 ();
  2         61  
  2         43  
33 2     2   1327 use File::Find::Rule 0.20 ();
  2         11388  
  2         60  
34 2     2   1176 use File::Slurp 9999.04 ();
  2         5552  
  2         51  
35 2     2   9299 use Class::Default 1.0 ();
  2         811  
  2         45  
36 2     2   773 use FileHandle 0 ();
  2         2970  
  2         56  
37 2     2   1758 use Params::Util 0.10 '_INSTANCE';
  2         8425  
  2         178  
38              
39 2     2   18 use vars qw{$VERSION @ISA};
  2         5  
  2         118  
40             BEGIN {
41 2     2   14 $VERSION = '1.12';
42 2         5626 @ISA = 'Class::Default';
43             }
44              
45              
46              
47              
48              
49             #####################################################################
50             # Constructor and Accessors
51              
52             =pod
53              
54             =head2 new param => value, ...
55              
56             The C<new> constructor creates a new conversion object.
57              
58             By default, the conversion object will process all files and convert
59             them to the local platform's newline format.
60              
61             Takes some optional parameters
62              
63             =over
64              
65             =item filter =E<gt> File::Find::Rule
66              
67             The C<filter> param allows you to provide an instantiate
68             L<File::Find::Rule> object, that will used to determine the list of
69             files to check or process.
70              
71             =item newline =E<gt> $newline
72              
73             The C<newline> option allows you to provide an alternative newline
74             format to the local one. The newline format should be provided as a
75             literal string.
76              
77             For example, to force Win32 newlines, you would use
78              
79             my $Object = File::LocalizeNewlines->new( newline => "\015\012" );
80              
81             =item verbose =E<gt> 1
82              
83             The C<verbose> option will cause the C<File::LocalizeNewlines> object to
84             print status information to C<STDOUT> as it runs.
85              
86             =back
87              
88             Returns a new C<File::LocalizeNewlines> object.
89              
90             =cut
91              
92             sub new {
93 11 50   11 1 12787 my $class = ref $_[0] ? ref shift : shift;
94 11         34 my %args = @_;
95              
96             # Create the basic object
97 11         38 my $self = bless { }, $class;
98              
99             # Check the file filter
100 11 100       193 if ( _INSTANCE($args{filter}, 'File::Find::Rule') ) {
101 5         17 $self->{Find} = $args{filter};
102 5         189 $self->{Find}->file->relative;
103             }
104              
105             # Allow for a custom platform
106 11 100       250 $self->{newline} = $args{newline} if $args{newline};
107              
108             # Check the verbose mode
109 11 50       48 if ( _CAN($args{verbose}, 'print') ) {
    50          
110 0         0 $self->{verbose} = $args{verbose};
111             } elsif ( $args{verbose} ) {
112 0         0 $self->{verbose} = 1;
113             }
114              
115 11         43 $self;
116             }
117              
118             =pod
119              
120             =head2 Find
121              
122             The C<Find> accessor returns the L<File::Find::Rule> object that will be
123             used for the file search.
124              
125             =cut
126              
127             sub Find {
128 11     11 1 5635 my $self = $_[0]->_self;
129 11 100       254 $self->{Find} or File::Find::Rule->file->relative;
130             }
131              
132             =pod
133              
134             =head2 newline
135              
136             The C<newline> accessor returns the newline format that will be used in
137             the localisation process.
138              
139             =cut
140              
141             sub newline {
142 22 100   22 1 1238 $_[0]->_self->{newline} or "\n";
143             }
144              
145              
146              
147              
148              
149             #####################################################################
150             # Methods
151              
152             =pod
153              
154             =head2 localized $file
155              
156             The C<localized> method takes an argument of a single file name or
157             file handle and tests it to see it is localized correctly.
158              
159             Returns true if localized correctly, false if not, or C<undef> on error.
160              
161             =cut
162              
163             sub localized {
164 15     15 1 866 my $self = shift->_self;
165 15 50 66     379 my $file = (defined $_[0] and ref $_[0]) ? shift
    100 33        
166             : (defined $_[0] and -f $_[0]) ? shift
167             : return undef;
168 15         38 my $newline = $self->newline;
169 15         140 my $content = File::Slurp::read_file( $file );
170              
171             # Create the localized version of the file
172 15         1484 my $localized = $content;
173 15         133 $localized =~ s/(?:\015{1,2}\012|\015|\012)/$newline/sg;
174              
175 15         85 $localized eq $content;
176             }
177              
178             =pod
179              
180             =head2 find $dir
181              
182             The C<find> method takes the path for a dir (or file) and returns a list
183             of relative files names for all of the files that do B<not> have their
184             newlines correctly localized.
185              
186             Returns a list of file names, or the null list if there are no files,
187             or if an incorrect path was provided.
188              
189             =cut
190              
191             sub find {
192 3     3 1 1887 my $self = shift->_self;
193 3 50       22 my $path = _DIRECTORY(shift) or return ();
194              
195             # Find all the files to test
196 3         9 my @files = $self->Find->in( $path );
197 7         102 @files = grep {
198 3         2565 ! $self->localized(
199             File::Spec->catfile( $path, $_ )
200             )
201             } @files;
202              
203 3         12 @files;
204             }
205              
206             =pod
207              
208             =head2 localize $file | $dir
209              
210             The C<localize> method takes a file, file handle or directory as argument
211             and localizes the newlines of the file, or all files within the directory
212             (that match the filter if one was provided).
213              
214             Returns the number of files that were localized, zero if no files needed to
215             be localized, or C<undef> on error.
216              
217             =cut
218              
219             sub localize {
220 3     3 1 1546 my $self = shift->_self;
221 3 50 33     114 my $path = (defined $_[0] and ref $_[0]) ? shift
    50 33        
222             : (defined $_[0] and -e $_[0]) ? shift
223             : return undef;
224              
225             # Switch on file or dir
226 3 100 66     57 (-f $path or ref $_[0])
227             ? $self->_localize_file( $path )
228             : $self->_localize_dir( $path );
229             }
230              
231             sub _localize_dir {
232 2     2   9 my $self = shift->_self;
233 2 50       13 my $path = _DIRECTORY(shift) or return undef;
234              
235             # Find the files to localise
236 2         8 my @files = $self->Find->in( $path );
237              
238             # Localize the files
239 2         1929 my $count = 0;
240 2         9 my $newline = $self->newline;
241 2         22 foreach ( @files ) {
242 4         57 my $file = File::Spec->catfile( $path, $_ );
243 4         19 my $content = File::Slurp::read_file( $file );
244 4         1098 my $localized = $content;
245 4         39 $localized =~ s/(?:\015{1,2}\012|\015|\012)/$newline/sg;
246 4 100       19 next if $localized eq $content;
247 3 50       13 File::Slurp::write_file( $file, $localized ) or return undef;
248 3         634 $self->_message( "Localized $file\n" );
249 3         9 $count++;
250             }
251              
252 2         17 $count;
253             }
254              
255             sub _localize_file {
256 1     1   5 my $self = shift->_self;
257 1 50 33     39 my $file = (defined $_[0] and ref $_[0]) ? shift
    50 33        
258             : (defined $_[0] and -f $_[0]) ? shift
259             : return undef;
260              
261             # Does the file need to be localised
262 1         5 my $newline = $self->newline;
263 1         15 my $content = File::Slurp::read_file( $file );
264 1         90 my $localized = $content;
265 1         15 $localized =~ s/(?:\015{1,2}\012|\015|\012)/$newline/sg;
266 1 50       5 return 0 if $content eq $localized;
267              
268             # Save the localised version
269 1 50       6 File::Slurp::write_file( $file, $localized ) or return undef;
270 1 50       294 $self->_message( "Localized $file\n" ) unless ref $file;
271              
272 1         7 1;
273             }
274              
275             sub _message {
276 4     4   21 my $self = shift;
277 4 50       16 return 1 unless defined $self->{verbose};
278 0         0 my $message = shift;
279 0 0       0 $message .= "\n" unless $message =~ /\n$/;
280 0 0       0 if ( _CAN( $self->{verbose}, 'print' ) ) {
281 0         0 $self->{verbose}->print( $message );
282             } else {
283 0         0 print STDOUT $message;
284             }
285             }
286              
287             sub _CAN {
288 11 50 33 11   93 (_INSTANCE($_[0], 'UNIVERSAL') and $_[0]->can($_[1])) ? shift : undef;
289             }
290              
291             sub _DIRECTORY {
292 5 50 33 5   123 (defined $_[0] and -d $_[0]) ? shift : undef;
293             }
294              
295             1;
296              
297             =pod
298              
299             =head1 SUPPORT
300              
301             Bugs should always be submitted via the CPAN bug tracker
302              
303             L<http://rt.cpan.org/NoAuth/ReportBug.html?Queue=File-LocalizeNewlines>
304              
305             For other issues, contact the maintainer.
306              
307             =head1 AUTHOR
308              
309             Adam Kennedy E<lt>adamk@cpan.orgE<gt>
310              
311             =head1 ACKNOWLEDGEMENTS
312              
313             Thank you to Phase N (L<http://phase-n.com/>) for permitting
314             the open sourcing and release of this distribution.
315              
316             L<FileHandle> support added by David Dick E<lt>ddick@cpan.orgE<gt>
317              
318             =head1 COPYRIGHT
319              
320             Copyright 2005 - 2009 Adam Kennedy.
321              
322             This program is free software; you can redistribute
323             it and/or modify it under the same terms as Perl itself.
324              
325             The full text of the license can be found in the
326             LICENSE file included with this module.
327              
328             =cut