File Coverage

blib/lib/Media/Type/Simple.pm
Criterion Covered Total %
statement 111 114 97.3
branch 32 38 84.2
condition 6 8 75.0
subroutine 24 24 100.0
pod 13 13 100.0
total 186 197 94.4


line stmt bran cond sub pod time code
1             package Media::Type::Simple;
2              
3 3     3   36560 use v5.10.0;
  3         9  
  3         112  
4              
5 3     3   14 use strict;
  3         4  
  3         75  
6 3     3   17 use warnings;
  3         4  
  3         78  
7              
8 3     3   12 use Carp;
  3         3  
  3         197  
9 3     3   1880 use Exporter::Lite;
  3         2631  
  3         17  
10 3     3   1725 use File::Share qw/ dist_file /;
  3         22208  
  3         239  
11 3     3   2028 use Storable qw/ dclone /;
  3         8379  
  3         177  
12              
13 3     3   1313 use version 0.77; our $VERSION = version->declare('v0.31.0');
  3         4241  
  3         20  
14              
15             our @EXPORT = qw( is_type alt_types ext_from_type ext3_from_type is_ext type_from_ext );
16             our @EXPORT_OK = (@EXPORT, qw/ add_type /);
17              
18             # TODO - option to disable reading of MIME types with no associated extensions
19              
20             =head1 NAME
21              
22             Media::Type::Simple - MIME Types and their file extensions
23              
24             =begin readme
25              
26             =head1 REQUIREMENTS
27              
28             The following non-core modules are required:
29              
30             Exporter::Lite
31             File::Share
32             File::ShareDir
33              
34             =end readme
35              
36             =head1 SYNOPSIS
37              
38             use Media::Type::Simple;
39              
40             $type = type_from_ext("jpg"); # returns "image/jpeg"
41              
42             $ext = ext_from_type("text/plain"); # returns "txt"
43              
44             =head1 DESCRIPTION
45              
46             This package gives a simple functions for obtaining common file
47             extensions from media types, and from obtaining media types from
48             file extensions.
49              
50             It is also relaxed with respect to having multiple media types
51             associated with a file extension, or multiple extensions associated
52             with a media type, and it includes media types for encodings such
53             as C. It is defined this way in the default data, but
54             this does not meet your needs, then you can have it use a system file
55             (e.g. F) or custom data.
56              
57             By default, there is a functional interface, although you can also use
58             an object-oriented interface. (Different objects will not share the
59             same data.)
60              
61             =for readme stop
62              
63             =head2 Methods
64              
65             =cut
66              
67             my $Default; # Pristine copy of data_
68             my $Work; # Working copy of data
69              
70             =over
71              
72             =item new
73              
74             $o = Media::Type::Simple->new;
75              
76             Creates a new object. You may optionally give it a filehandle of a file
77             with system Media information, e.g.
78              
79             open $f, "/etc/mime.types";
80             $o = Media::Type::Simple->new( $f );
81              
82             =begin internal
83              
84             When L is called for the first time without a file handle, it
85             checks to see if the C<$Default> instance is initialised: if it is
86             not, then it initialises it and returns a L of C<$Default>.
87              
88             We operate on clones rather than the original, so that any changes
89             made, e.g. L, will not affect all other instances.
90              
91             =end internal
92              
93             =cut
94              
95             sub new {
96 3     3 1 430 my $class = shift;
97 3         23 my $self = { types => { }, extens => { }, };
98              
99 3         11 bless $self, $class;
100              
101 3 50       26 if (@_) {
102 0         0 my $fh = shift;
103 0         0 return $self->add_types_from_file( $fh );
104             }
105             else {
106 3 100       8 unless (defined $Default) {
107 2         23 my $file = dist_file('Media-Type-Simple', 'mime.types');
108 2 50       955 open my $fh, '<', $file
109             or croak "Unable to open ${file}: $!";
110 2         7 $Default = $self->add_types_from_file( $fh );
111 2         68 close $fh;
112             }
113 3         12 return clone $Default;
114             }
115             }
116              
117             =begin internal
118              
119             =item _args
120              
121             An internal function used to process arguments, based on C<_args> from
122             the L package. It also allows one to use it in non-object
123             oriented mode.
124              
125             When L is called for the first time without a reference to the
126             class instance, it checks to see if C<$Work> is defined, and it is
127             initialised with L if it is not defined. This means that
128             C<$Work> is only initialised when the module is used.
129              
130             =item self
131              
132             An internal function used in place of the C<$self> variable.
133              
134             =item args
135              
136             An internal function used in place of shifting arguments from stack.
137              
138             =end internal
139              
140             =cut
141              
142             # _args, self and args based on 'self' v0.15
143              
144             sub _args {
145 7438     7438   5635 my $level = 2;
146 7438         6423 my @c = ();
147 7438   66     13544 while ( !defined($c[3]) || $c[3] eq '(eval)') {
148 7438         5285 @c = do {
149             package DB; # Module::Build hates this!
150 7438         6207 @DB::args = ();
151 7438         30313 caller($level);
152             };
153 7438         26880 $level++;
154             }
155              
156 7438         9966 my @args = @DB::args;
157              
158 7438 100       11458 if (ref($args[0]) ne __PACKAGE__) {
159 61 100       82 unless (defined $Work) {
160 2         24 $Work = __PACKAGE__->new();
161             }
162 61         68 unshift @args, $Work;
163             }
164              
165 7438         28563 return @args;
166             }
167              
168             sub self {
169 6560     6560 1 6770 (_args)[0];
170             }
171              
172             sub args {
173 878     878 1 1160 my @a = _args;
174 878         2243 return @a[1..$#a];
175             }
176              
177              
178             =item add_types_from_file
179              
180             $o->add_types_from_file( $filehandle );
181              
182             Imports types from a file. Called by L when a filehandle is
183             specified.
184              
185             =cut
186              
187             sub add_types_from_file {
188 2     2 1 22 my ($fh) = args;
189              
190 2         45 while (my $line = <$fh>) {
191 792         1512 $line =~ s/^\s+//;
192 792         846 $line =~ s/\#.*$//;
193 792         2403 $line =~ s/\s+$//;
194              
195 792 50       1299 if ($line) {
196 792         917 self->add_type(split /\s+/, $line);
197             }
198             }
199 2         6 return self;
200             }
201              
202             =item is_type
203              
204             if (is_type("text/plain")) { ... }
205              
206             if ($o->is_type("text/plain")) { ... }
207              
208             Returns a true value if the type is defined in the system.
209              
210             Note that a true value does not necessarily indicate that the type
211             has file extensions associated with it.
212              
213             =begin internal
214              
215             Currently it returns a reference to a list of extensions associated
216             with that type. This is for convenience, and may change in future
217             releases.
218              
219             =end internal
220              
221             =cut
222              
223             sub is_type {
224 53     53 1 1644 my ($type) = args;
225 53         66 my ($cat, $spec) = split_type($type);
226 53 100 66     149 return if ! defined $spec || ! length $spec;
227 50         52 return self->{types}->{$cat}->{$spec};
228             }
229              
230             =item alt_types
231              
232             @alts = alt_types("image/jpeg");
233              
234             @alts = $o->alt_types("image/jpeg");
235              
236             Returns alternative or related Media types that are defined in the system
237             For instance,
238              
239             alt_types("model/dwg")
240              
241             returns the list
242              
243             image/vnd.dwg
244              
245             =begin internal
246              
247             =item _normalise
248              
249             =item _add_aliases
250              
251             =end internal
252              
253             =cut
254              
255             {
256              
257             # Some known special cases (keys are normalised). Not exhaustive.
258              
259             my %SPEC_CASES = (
260             "audio/flac" => [qw( application/flac )],
261             "application/cdf" => [qw( application/netcdf )],
262             "application/dms" => [qw( application/octet-stream )],
263             "application/x-java-source" => [qw( text/plain )],
264             "application/java-vm" => [qw( application/octet-stream )],
265             "application/lha" => [qw( application/octet-stream )],
266             "application/lzh" => [qw( application/octet-stream )],
267             "application/mac-binhex40" => [qw( application/binhex40 )],
268             "application/msdos-program" => [qw( application/octet-stream )],
269             "application/ms-pki.seccat" => [qw( application/vnd.ms-pkiseccat )],
270             "application/ms-pki.stl" => [qw( application/vnd.ms-pki.stl )],
271             "application/ndtcdf" => [qw( application/cdf )],
272             "application/netfpx" => [qw( image/vnd.fpx image/vnd.net-fpx )],
273             "audio/ogg" => [qw( application/ogg )],
274             "image/fpx" => [qw( application/vnd.netfpx image/vnd.net-fpx )],
275             "image/netfpx" => [qw( application/vnd.netfpx image/vnd.fpx )],
276             "text/c++hdr" => [qw( text/plain )],
277             "text/c++src" => [qw( text/plain )],
278             "text/chdr" => [qw( text/plain )],
279             "text/fortran" => [qw( text/plain )],
280             );
281              
282              
283             sub _normalise {
284 146     146   99 my $type = shift;
285 146         139 my ($cat, $spec) = split_type($type);
286              
287             # We "normalise" the type
288              
289 146         133 $cat =~ s/^x-//;
290 146         173 $spec =~ s/^(x-|vnd\.)//;
291              
292 146         175 return ($cat, $spec);
293             }
294              
295             sub _add_aliases {
296 42     42   67 my @aliases = @_;
297 42         40 foreach my $type (@aliases) {
298 141         140 my ($cat, $spec) = _normalise($type);
299 141         275 $SPEC_CASES{"$cat/$spec"} = \@aliases;
300             }
301             }
302              
303             _add_aliases(qw( application/mp4 video/mp4 ));
304             _add_aliases(qw( application/json text/json ));
305             _add_aliases(qw( application/cals-1840 image/cals-1840 image/cals image/x-cals application/cals ));
306             _add_aliases(qw( application/mac-binhex40 application/binhex40 ));
307             _add_aliases(qw( application/atom+xml application/atom ));
308             _add_aliases(qw( application/fractals image/fif ));
309             _add_aliases(qw( model/vnd.dwg image/vnd.dwg image/x-dwg application/acad ));
310             _add_aliases(qw( image/vnd.dxf image/x-dxf application/x-dxf application/vnd.dxf ));
311             _add_aliases(qw( text/x-c text/csrc ));
312             _add_aliases(qw( application/x-helpfile application/x-winhlp ));
313             _add_aliases(qw( application/x-tex text/x-tex ));
314             _add_aliases(qw( application/rtf text/rtf ));
315             _add_aliases(qw( image/jpeg image/pipeg image/pjpeg ));
316             _add_aliases(qw( text/javascript text/javascript1.0 text/javascript1.1 text/javascript1.2 text/javascript1.3 text/javascript1.4 text/javascript1.5 text/jscript text/livescript text/x-javascript text/x-ecmascript aplication/ecmascript application/javascript ));
317              
318              
319             sub alt_types {
320 5     5 1 1275 my ($type) = args;
321 5         14 my ($cat, $spec) = _normalise($type);
322              
323 5         7 my %alts = ( );
324 5         22 my @cases = ( "$cat/$spec", "$cat/x-$spec", "x-$cat/x-$spec",
325             "$cat/vnd.$spec" );
326              
327 5 100       17 push @cases, @{ $SPEC_CASES{"$cat/$spec"} },
  4         10  
328             if ($SPEC_CASES{"$cat/$spec"});
329              
330 5         9 foreach ( @cases ) {
331 32 100       35 $alts{$_} = 1, if (self->is_type($_));
332             }
333              
334 5         29 return (sort keys %alts);
335             }
336             }
337              
338             =item ext_from_type
339              
340             $ext = ext_from_type( $type );
341              
342             @exts = ext_from_type( $type );
343              
344             $ext = $o->ext_from_type( $type );
345              
346             @exts = $o->ext_from_type( $type );
347              
348             Returns the file extension(s) associated with the given Media type.
349             When called in a scalar context, returns the first extension from the
350             list.
351              
352             The order of extensions is based on the order that they occur in the
353             source data (either the default here, or the order added using
354             L or calls to L).
355              
356             =cut
357              
358             sub ext_from_type {
359 11 100   11 1 2045 if (my $exts = self->is_type(args)) {
360 10 100       42 return (wantarray ? @$exts : $exts->[0]);
361             }
362             else {
363 1         4 return;
364             }
365             }
366              
367             =item ext3_from_type
368              
369             Like L, but only returns file extensions under three
370             characters long.
371              
372             =cut
373              
374             sub ext3_from_type {
375 4     4 1 1939 my @exts = grep( (length($_) <= 3), (ext_from_type(@_)));
376 4 100       16 return (wantarray ? @exts : $exts[0]);
377             }
378              
379             =item is_ext
380              
381             if (is_ext("jpeg")) { ... }
382              
383             if ($o->is_ext("jpeg")) { ... }
384              
385             Returns a true value if the extension is defined in the system.
386              
387             =begin internal
388              
389             Currently it returns a reference to a list of types associated
390             with that extension. This is for convenience, and may change in future
391             releases.
392              
393             =end internal
394              
395             =cut
396              
397             sub is_ext {
398 8     8 1 868 my ($ext) = args;
399 8 100       16 if (exists self->{extens}->{$ext}) {
400 7         14 return self->{extens}->{$ext};
401             }
402             else {
403 1         3 return;
404             }
405             }
406              
407             =item type_from_ext
408              
409             $type = type_from_ext( $extension );
410              
411             @types = type_from_ext( $extension );
412              
413             $type = $o->type_from_ext( $extension );
414              
415             @types = $o->type_from_ext( $extension );
416              
417             Returns the Media type(s) associated with the extension. When called
418             in a scalar context, returns the first type from the list.
419              
420             The order of types is based on the order that they occur in the
421             source data (either the default here, or the order added using
422             L or calls to L).
423              
424             =cut
425              
426             sub type_from_ext {
427 5     5 1 2146 my ($ext) = args;
428              
429 5 50       11 if (my $ts = self->is_ext($ext)) {
430 5         9 my @types = map { $_ } @$ts;
  15         23  
431 5 100       37 return (wantarray ? @types : $types[0]);
432             }
433             else {
434 0         0 croak "Unknown extension: $ext";
435             }
436             }
437              
438             =begin internal
439              
440             =item split_type
441              
442             ($content_type, $subtype) = split_type( $type );
443              
444             This is a utility function for splitting content types.
445              
446             =end internal
447              
448             =cut
449              
450             sub split_type {
451 993     993 1 1004 my $type = shift;
452 993         1676 my ($cat, $spec) = split /\//, $type;
453 993         1595 return ($cat, $spec);
454             }
455              
456             =item add_type
457              
458             $o->add_type( $type, @extensions );
459              
460             Add a type to the system, with an optional list of extensions.
461              
462             =cut
463              
464             sub add_type {
465 794     794 1 1971 my ($type, @exts) = args;
466              
467 794 50 100     1991 if (@exts || 1) { # TODO - option to ignore types with no extensions
468              
469 794         1032 my ($cat, $spec) = split_type($type);
470              
471 794 100       1031 if (!self->{types}->{$cat}->{$spec}) {
472 791         1258 self->{types}->{$cat}->{$spec} = [ ];
473             }
474 794         929 push @{ self->{types}->{$cat}->{$spec} }, @exts;
  794         903  
475              
476              
477 794         1259 foreach (@exts) {
478 1120 100       1264 self->{extens}->{$_} = [] unless (exists self->{extens}->{$_});
479 1120         1369 push @{self->{extens}->{$_}}, $type
  1120         1212  
480             }
481             }
482             }
483              
484             =item clone
485              
486             $c = $o->clone;
487              
488             Returns a clone of a Media::Type::Simple object. This allows you to add
489             new types via L or L without affecting
490             the original.
491              
492             This can I be used in the object-oriented interface.
493              
494             =cut
495              
496             sub clone {
497 4     4 1 511 my $self = shift;
498 4 50       14 croak "Expected instance" if (ref($self) ne __PACKAGE__);
499 4         5464 return dclone( $self );
500             }
501              
502              
503             =back
504              
505             =for readme continue
506              
507             =head1 REVISION HISTORY
508              
509             For a detailed history see the F file included in this
510             distribution.
511              
512             =head1 SEE ALSO
513              
514             The L module has a similar functionality, but with a more
515             complex interface.
516              
517             L will guess the media type from a file extension,
518             attempting to use the F<~/.media.types> file.
519              
520             An "official" list of Media Types can be found at
521             L.
522              
523             =head1 AUTHOR
524              
525             Robert Rothenberg
526              
527             =head2 Contributors
528              
529             =over
530              
531             =item Russell Jenkins
532              
533             =item Martin McGrath
534              
535             =back
536              
537             =head2 Acknowledgements
538              
539             Some of the code comes from L module (by Kang-min Liu). The data
540             for the media types is based on the Debian C package,
541             L,
542             although with I changes from the original.
543              
544             =head2 Suggestions and Bug Reporting
545              
546             Feedback is always welcome. Please use the CPAN Request Tracker at
547             L to submit bug reports.
548              
549             The git repository for this module is at
550             L.
551              
552             =head1 COPYRIGHT & LICENSE
553              
554             Copyright 2009-2015 Robert Rothenberg, all rights reserved.
555              
556             This program is free software; you can redistribute it and/or modify it
557             under the same terms as Perl itself.
558              
559             =cut
560              
561             1;
562              
563