diff options
author | gregor herrmann <gregoa@debian.org> | 2020-10-31 01:37:35 +0100 |
---|---|---|
committer | gregor herrmann <gregoa@debian.org> | 2020-10-31 01:37:35 +0100 |
commit | 931d852d404398a0b3c8aacc16f1b012b5ac8461 (patch) | |
tree | 671bcef27936aabe70c1b55307d1bd8a0c2e4ac7 | |
parent | 1c430a92837eb5ba044910d01203eef67b15138c (diff) | |
parent | e81ee0e34865a80770d1745d73e81afca486ede2 (diff) |
New upstream version 0.30
-rw-r--r-- | Changes | 37 | ||||
-rw-r--r-- | META.json | 12 | ||||
-rw-r--r-- | META.yml | 8 | ||||
-rw-r--r-- | Makefile.PL | 4 | ||||
-rw-r--r-- | README.md | 6 | ||||
-rw-r--r-- | lib/File/MimeInfo.pm | 461 | ||||
-rw-r--r-- | lib/File/MimeInfo/Applications.pm | 307 | ||||
-rw-r--r-- | lib/File/MimeInfo/Magic.pm | 376 | ||||
-rw-r--r-- | lib/File/MimeInfo/Rox.pm | 101 | ||||
-rwxr-xr-x | mimeopen | 314 | ||||
-rwxr-xr-x | mimetype | 300 | ||||
-rw-r--r-- | t/000-report-versions-tiny.t | 5 | ||||
-rw-r--r-- | t/00_use_ok.t | 6 | ||||
-rw-r--r-- | t/01_normal.t | 86 | ||||
-rw-r--r-- | t/02_magic.t | 12 | ||||
-rw-r--r-- | t/03_rox.t | 12 | ||||
-rw-r--r-- | t/04_IO_objects.t | 28 | ||||
-rw-r--r-- | t/05_more.t | 83 | ||||
-rw-r--r-- | t/06_pod_ok.t | 2 | ||||
-rw-r--r-- | t/10filehandle.t | 4 | ||||
-rw-r--r-- | t/11mimeinfo.t | 15 |
21 files changed, 1148 insertions, 1031 deletions
@@ -2,13 +2,26 @@ Revision history for Perl extension File::MimeInfo. Versions up to 0.15 by Jaap Karssenberg <pardus@cpan.org> Versions starting 0.16 by Michiel Beijen <michiel.beijen@gmail.com> +0.30 2020-10-26 + - Added function File::MimeInfo::has_mimeinfo_database() to test if + mimeinfo database is found + - mimetype and mimeopen now exit with an error code if no mimeinfo database + is found + - Added explicit dependency on Encode::Locale + - Properly decode parameters for `mimeopen` and `mimetype`. + Patch provided by Nikos Skalkotos + - Fix for undefined defaults (fixes #36). + Reported & fix provided by Martin Vassor + - Improved installation instructions, specifically on macOS, by + Oleg Kostyuk & Michiel Beijen + 0.29 2018-08-05 - Stable release to CPAN. 0.28_03 2017-01-22 - `mimeinfo --stdin` did not work. Reported by Marius Gavrilescu, - Debian bug https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=784545 - Added minimal tests for `mimeinfo`. + Debian bug https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=784545 + Added minimal tests for `mimeinfo`. 0.28_02 2016-12-17 - Improved handling of Path::Tiny objects in default method. @@ -16,21 +29,21 @@ Versions starting 0.16 by Michiel Beijen <michiel.beijen@gmail.com> 0.28_01 2016-11-29 - Spelling fix courtesy gregor herrmann, Debian Perl group (fixes #25) - Added t/000-report-versions-tiny.t to find out issue with Path::Tiny file - handles. + handles. 0.28 2016-11-27 - Follow the current mime-apps-spec (fixes #8, #20) - The current version of the mime-apps spec locates the per-user defaults - file in `$XDG_CONFIG_HOME/mimeapps.list`. Use that location, and fall - back to the per-system and distribution defaults as specified, with the - previous legacy defaults file as a final fallback. - Fix by Patrick Burroughs (Celti) + The current version of the mime-apps spec locates the per-user defaults + file in `$XDG_CONFIG_HOME/mimeapps.list`. Use that location, and fall + back to the per-system and distribution defaults as specified, with the + previous legacy defaults file as a final fallback. + Fix by Patrick Burroughs (Celti) - POD clarifications by Nitish Bezzala 0.27 2015-02-23 - Allow to use mimeinfo on a Path::Tiny object. Reported by Smylers. - Misspelled NoDisplay attribute in .desktop file. - Fix by Bernhard Rosenkraenzer (berolinux) + Fix by Bernhard Rosenkraenzer (berolinux) - Fix typos in README by Sean Smith (ncstang) as part of CPAN Pull Request Challenge February 2015. @@ -47,8 +60,8 @@ Versions starting 0.16 by Michiel Beijen <michiel.beijen@gmail.com> 0.23 2014-04-02 - Fixed opening of files with '+' in mime type using - File::MimeInfo::Applications. - Debian bug 690334, brian m. carlson. + File::MimeInfo::Applications. + Debian bug 690334, brian m. carlson. 0.22 2014-02-27 - Fixed Double-close on a filehandle issue (RT 93221) - Christian Ludwig. @@ -143,7 +156,7 @@ Versions starting 0.16 by Michiel Beijen <michiel.beijen@gmail.com> 0.9 2003-12-05 - Fixed magic() and default() to work on IO::something objects - Added a "no warnings" in the default routine to suppress warnings when - input is latin2 (thus neither ascii or utf8). Not sure whether this + input is latin2 (thus neither ascii or utf8). Not sure whether this really fixes the problem but it at least ignores it. The problem was reported by Daniel Raska. @@ -4,13 +4,13 @@ "Jaap Karssenberg <pardus@cpan.org>" ], "dynamic_config" : 1, - "generated_by" : "ExtUtils::MakeMaker version 7.18, CPAN::Meta::Converter version 2.150010", + "generated_by" : "ExtUtils::MakeMaker version 7.44, CPAN::Meta::Converter version 2.150010", "license" : [ "perl_5" ], "meta-spec" : { "url" : "http://search.cpan.org/perldoc?CPAN::Meta::Spec", - "version" : "2" + "version" : 2 }, "name" : "File-MimeInfo", "no_index" : { @@ -23,6 +23,7 @@ "build" : { "requires" : { "Carp" : "0", + "Encode::Locale" : "0", "Exporter" : "0", "Fcntl" : "0", "File::BaseDir" : "0.03", @@ -55,15 +56,18 @@ "url" : "https://github.com/mbeijen/File-MimeInfo" } }, - "version" : "0.29", + "version" : "0.30", "x_contributors" : [ "Bernhard Rosenkränzer <bero@lindev.ch>", "Christian Ludwig <chrissicool@gmail.com>", "David Steinbrunner <dsteinbrunner@pobox.com>", "Jitka Plesnikova <jplesnik@redhat.com>", "Jonathan Dowland <jmtd@debian.org>", + "Martin Vassor <martin.vassor@alumni.epfl.ch>", "Michiel Beijen <michiel.beijen@gmail.com>", + "Nikos Skalkotos <skalkoto@grnet.gr>", "Nitish Bezzala <nbezzala@yahoo.com>", + "Oleg Kostyuk <cub.uanic@gmail.com>", "Patrick Burroughs (Celti) <celti@celti.name>", "Rudolf Leermakers <rudolf@hatsuseno.org>", "Sean Smith <ssmith@ncsgraphics.com>", @@ -71,5 +75,5 @@ "brian m. carlson <sandals@crustytoothpaste.net>", "gregor herrmann <gregoa@debian.org>" ], - "x_serialization_backend" : "JSON::PP version 2.27300" + "x_serialization_backend" : "JSON::PP version 4.04" } @@ -4,6 +4,7 @@ author: - 'Jaap Karssenberg <pardus@cpan.org>' build_requires: Carp: '0' + Encode::Locale: '0' Exporter: '0' Fcntl: '0' File::BaseDir: '0.03' @@ -13,7 +14,7 @@ build_requires: configure_requires: ExtUtils::MakeMaker: '6.30' dynamic_config: 1 -generated_by: 'ExtUtils::MakeMaker version 7.18, CPAN::Meta::Converter version 2.150010' +generated_by: 'ExtUtils::MakeMaker version 7.44, CPAN::Meta::Converter version 2.150010' license: perl meta-spec: url: http://module-build.sourceforge.net/META-spec-v1.4.html @@ -28,15 +29,18 @@ requires: resources: bugtracker: https://github.com/mbeijen/File-MimeInfo/issues repository: https://github.com/mbeijen/File-MimeInfo -version: '0.29' +version: '0.30' x_contributors: - 'Bernhard Rosenkränzer <bero@lindev.ch>' - 'Christian Ludwig <chrissicool@gmail.com>' - 'David Steinbrunner <dsteinbrunner@pobox.com>' - 'Jitka Plesnikova <jplesnik@redhat.com>' - 'Jonathan Dowland <jmtd@debian.org>' + - 'Martin Vassor <martin.vassor@alumni.epfl.ch>' - 'Michiel Beijen <michiel.beijen@gmail.com>' + - 'Nikos Skalkotos <skalkoto@grnet.gr>' - 'Nitish Bezzala <nbezzala@yahoo.com>' + - 'Oleg Kostyuk <cub.uanic@gmail.com>' - 'Patrick Burroughs (Celti) <celti@celti.name>' - 'Rudolf Leermakers <rudolf@hatsuseno.org>' - 'Sean Smith <ssmith@ncsgraphics.com>' diff --git a/Makefile.PL b/Makefile.PL index 6ac80e9..511935b 100644 --- a/Makefile.PL +++ b/Makefile.PL @@ -16,6 +16,7 @@ WriteMakefile ( 'BUILD_REQUIRES' => { 'Carp' => 0, 'Exporter' => 0, + 'Encode::Locale' => 0, 'Fcntl' => 0, 'Pod::Usage' => 0, 'File::BaseDir' => '0.03', @@ -50,8 +51,11 @@ WriteMakefile ( 'David Steinbrunner <dsteinbrunner@pobox.com>', 'Jitka Plesnikova <jplesnik@redhat.com>', 'Jonathan Dowland <jmtd@debian.org>', + 'Martin Vassor <martin.vassor@alumni.epfl.ch>', 'Michiel Beijen <michiel.beijen@gmail.com>', + 'Nikos Skalkotos <skalkoto@grnet.gr>', 'Nitish Bezzala <nbezzala@yahoo.com>', + 'Oleg Kostyuk <cub.uanic@gmail.com>', 'Patrick Burroughs (Celti) <celti@celti.name>', 'Rudolf Leermakers <rudolf@hatsuseno.org>', 'Sean Smith <ssmith@ncsgraphics.com>', @@ -6,6 +6,12 @@ trying to implement the freedesktop specification for using the shared mime-info database. The package comes with a script called `mimetype` that can be used as a `file(1)` work-alike. +Because this module uses the FreeDesktop MIME database, it's most +suited for use in perl scripts that run on a desktop OS; in fact, +this module is typically pre-installed on Debian and Ubuntu OSes +so you can use its command line script `mimeopen` to open files in +the GUI just like you can use `open` on macOS. + ## INSTALLATION To install this module type the following: diff --git a/lib/File/MimeInfo.pm b/lib/File/MimeInfo.pm index 0f0dd86..23b149a 100644 --- a/lib/File/MimeInfo.pm +++ b/lib/File/MimeInfo.pm @@ -1,6 +1,7 @@ package File::MimeInfo; use strict; +use warnings; use Carp; use Fcntl 'SEEK_SET'; use File::Spec; @@ -10,10 +11,10 @@ require Exporter; our @ISA = qw(Exporter); our @EXPORT = qw(mimetype); our @EXPORT_OK = qw(extensions describe globs inodetype mimetype_canon mimetype_isa); -our $VERSION = '0.29'; +our $VERSION = '0.30'; our $DEBUG; -our ($_hashed, $_hashed_aliases, $_hashed_subclasses); +our ($_hashed, $_hashed_aliases, $_hashed_subclasses, $_has_mimeinfo_database); our (@globs, %literal, %extension, %mime2ext, %aliases, %subclasses); our ($LANG, @DIRS); # @globs = [ [ 'glob', qr//, $mime_string ], ... ] @@ -28,263 +29,270 @@ our ($LANG, @DIRS); sub new { bless \$VERSION, shift } # what else is there to bless ? sub mimetype { - my $file = pop; - croak 'subroutine "mimetype" needs a filename as argument' unless defined $file; - return - inodetype($file) || - globs($file) || - default($file); + my $file = pop; + croak 'subroutine "mimetype" needs a filename as argument' unless defined $file; + return + inodetype($file) || + globs($file) || + default($file); } sub inodetype { - my $file = pop; - print STDERR "> Checking inode type\n" if $DEBUG; - lstat $file or return undef; - return undef if -f _; - my $t = (-l $file) ? 'inode/symlink' : # Win32 does not like '_' here - (-d _) ? 'inode/directory' : - (-p _) ? 'inode/fifo' : - (-c _) ? 'inode/chardevice' : - (-b _) ? 'inode/blockdevice' : - (-S _) ? 'inode/socket' : '' ; - if ($t eq 'inode/directory') { # compare devices to detect mount-points - my $dev = (stat _)[0]; # device of the node under investigation - $file = File::Spec->rel2abs($file); # get full path - my @dirs = File::Spec->splitdir($file); - $file = File::Spec->catfile(@dirs); # removes trailing '/' or equivalent - return $t if -l $file; # parent can be on other dev for links - pop @dirs; - my $dir = File::Spec->catdir(@dirs); # parent dir - $t = 'inode/mount-point' unless (stat $dir)[0] == $dev; # compare devices - return $t; - } - else { return $t ? $t : undef } + my $file = pop; + print STDERR "> Checking inode type\n" if $DEBUG; + lstat $file or return undef; + return undef if -f _; + my $t = (-l $file) ? 'inode/symlink' : # Win32 does not like '_' here + (-d _) ? 'inode/directory' : + (-p _) ? 'inode/fifo' : + (-c _) ? 'inode/chardevice' : + (-b _) ? 'inode/blockdevice' : + (-S _) ? 'inode/socket' : '' ; + if ($t eq 'inode/directory') { # compare devices to detect mount-points + my $dev = (stat _)[0]; # device of the node under investigation + $file = File::Spec->rel2abs($file); # get full path + my @dirs = File::Spec->splitdir($file); + $file = File::Spec->catfile(@dirs); # removes trailing '/' or equivalent + return $t if -l $file; # parent can be on other dev for links + pop @dirs; + my $dir = File::Spec->catdir(@dirs); # parent dir + $t = 'inode/mount-point' unless (stat $dir)[0] == $dev; # compare devices + return $t; + } + else { return $t ? $t : undef } } sub globs { - my $file = pop; - croak 'subroutine "globs" needs a filename as argument' unless defined $file; - rehash() unless $_hashed; - (undef, undef, $file) = File::Spec->splitpath($file); # remove path - print STDERR "> Checking globs for basename '$file'\n" if $DEBUG; - - return $literal{$file} if exists $literal{$file}; - - if ($file =~ /\.(\w+(\.\w+)*)$/) { - my @ext = split /\./, $1; - while (@ext) { - my $ext = join('.', @ext); - print STDERR "> Checking for extension '.$ext'\n" if $DEBUG; - warn "WARNING: wantarray behaviour of globs() will change in the future.\n" if wantarray; - return wantarray - ? ($extension{$ext}, $ext) - : $extension{$ext} - if exists $extension{$ext}; - shift @ext; - } - } - - for (@globs) { - next unless $file =~ $_->[1]; - print STDERR "> This file name matches \"$_->[0]\"\n" if $DEBUG; - return $_->[2]; - } - - return globs(lc $file) if $file =~ /[A-Z]/; # recurs - return undef; + my $file = pop; + croak 'subroutine "globs" needs a filename as argument' unless defined $file; + rehash() unless $_hashed; + (undef, undef, $file) = File::Spec->splitpath($file); # remove path + print STDERR "> Checking globs for basename '$file'\n" if $DEBUG; + + return $literal{$file} if exists $literal{$file}; + + if ($file =~ /\.(\w+(\.\w+)*)$/) { + my @ext = split /\./, $1; + while (@ext) { + my $ext = join('.', @ext); + print STDERR "> Checking for extension '.$ext'\n" if $DEBUG; + carp "WARNING: wantarray behaviour of globs() will change in the future.\n" if wantarray; + return wantarray + ? ($extension{$ext}, $ext) + : $extension{$ext} + if exists $extension{$ext}; + shift @ext; + } + } + + for (@globs) { + next unless $file =~ $_->[1]; + print STDERR "> This file name matches \"$_->[0]\"\n" if $DEBUG; + return $_->[2]; + } + + return globs(lc $file) if $file =~ /[A-Z]/; # recurs + return undef; } sub default { - my $file = pop; - croak 'subroutine "default" needs a filename as argument' unless defined $file; - - my $line; - unless (ref $file) { - return undef unless -f $file; - print STDERR "> File exists, trying default method\n" if $DEBUG; - return 'text/plain' if -z $file; - - open FILE, '<', $file or return undef; - binmode FILE, ':utf8' unless $] < 5.008; - read FILE, $line, 32; - close FILE; - } - elsif (ref $file eq 'Path::Tiny') { - return undef unless $file->exists; - print STDERR "> File is Path::Tiny object and exists, " - . "trying default method\n" if $DEBUG; - open my $fh, '<', $file or return undef; - binmode FILE, ':utf8' unless $] < 5.008; - read $fh, $line, 32; - close $fh; - } - else { - print STDERR "> Trying default method on object\n" if $DEBUG; - - $file->seek(0, SEEK_SET); - $file->read($line, 32); - } - - { - no warnings; # warnings can be thrown when input not ascii - if ($] < 5.008 or ! utf8::valid($line)) { - use bytes; # avoid invalid utf8 chars - $line =~ s/\s//g; # \m, \n and \t are also control chars - return 'text/plain' unless $line =~ /[\x00-\x1F\x7F]/; - } - else { - # use perl to do something intelligent for ascii & utf8 - return 'text/plain' unless $line =~ /[^[:print:]\s]/; - } - } - print STDERR "> First 10 bytes of the file contain control chars\n" if $DEBUG; - return 'application/octet-stream'; + my $file = pop; + croak 'subroutine "default" needs a filename as argument' unless defined $file; + + my $line; + unless (ref $file) { + return undef unless -f $file; + print STDERR "> File exists, trying default method\n" if $DEBUG; + return 'text/plain' if -z $file; + + open FILE, '<', $file or return undef; + binmode FILE, ':utf8' unless $] < 5.008; + read FILE, $line, 32; + close FILE; + } + elsif (ref $file eq 'Path::Tiny') { + return undef unless $file->exists; + print STDERR "> File is Path::Tiny object and exists, " + . "trying default method\n" if $DEBUG; + open my $fh, '<', $file or return undef; + binmode FILE, ':utf8' unless $] < 5.008; + read $fh, $line, 32; + close $fh; + } + else { + print STDERR "> Trying default method on object\n" if $DEBUG; + + $file->seek(0, SEEK_SET); + $file->read($line, 32); + } + + { + no warnings; # warnings can be thrown when input not ascii + if ($] < 5.008 or ! utf8::valid($line)) { + use bytes; # avoid invalid utf8 chars + $line =~ s/\s//g; # \m, \n and \t are also control chars + return 'text/plain' unless $line =~ /[\x00-\x1F\x7F]/; + } + else { + # use perl to do something intelligent for ascii & utf8 + return 'text/plain' unless $line =~ /[^[:print:]\s]/; + } + } + print STDERR "> First 10 bytes of the file contain control chars\n" if $DEBUG; + return 'application/octet-stream'; } sub rehash { - (@globs, %literal, %extension, %mime2ext) = (); # clear all data - local $_; # limit scope of $_ ... :S - my @globfiles = @DIRS - ? ( grep {-e $_ && -r $_} map "$_/globs", @DIRS ) - : ( reverse data_files('mime/globs') ); - print STDERR << 'EOT' unless @globfiles; -WARNING: You don't seem to have a mime-info database. The -shared-mime-info package is available from http://freedesktop.org/ . -EOT - my @done; - for my $file (@globfiles) { - next if grep {$file eq $_} @done; - _hash_globs($file); - push @done, $file; - } - $_hashed = 1; + (@globs, %literal, %extension, %mime2ext) = (); # clear all data + local $_; # limit scope of $_ ... :S + my @globfiles = @DIRS + ? ( grep {-e $_ && -r $_} map "$_/globs", @DIRS ) + : ( reverse data_files('mime/globs') ); + if (@globfiles) { + $_has_mimeinfo_database = 1; + } else { + carp "WARNING: You don't seem to have a mime-info database. " . + "The shared-mime-info package is available from http://freedesktop.org/"; + } + my @done; + for my $file (@globfiles) { + next if grep {$file eq $_} @done; + _hash_globs($file); + push @done, $file; + } + $_hashed = 1; } sub _hash_globs { - my $file = shift; - open GLOB, '<', $file || croak "Could not open file '$file' for reading" ; - binmode GLOB, ':utf8' unless $] < 5.008; - my ($string, $glob); - while (<GLOB>) { - next if /^\s*#/ or ! /\S/; # skip comments and empty lines - chomp; - ($string, $glob) = split /:/, $_, 2; - unless ($glob =~ /[\?\*\[]/) { $literal{$glob} = $string } - elsif ($glob =~ /^\*\.(\w+(\.\w+)*)$/) { - $extension{$1} = $string unless exists $extension{$1}; - $mime2ext{$string} = [] if !defined($mime2ext{$string}); - push @{$mime2ext{$string}}, $1; - } else { unshift @globs, [$glob, _glob_to_regexp($glob), $string] } - } - close GLOB || croak "Could not open file '$file' for reading" ; + my $file = shift; + open GLOB, '<', $file || croak "Could not open file '$file' for reading" ; + binmode GLOB, ':utf8' unless $] < 5.008; + my ($string, $glob); + while (<GLOB>) { + next if /^\s*#/ or ! /\S/; # skip comments and empty lines + chomp; + ($string, $glob) = split /:/, $_, 2; + unless ($glob =~ /[\?\*\[]/) { $literal{$glob} = $string } + elsif ($glob =~ /^\*\.(\w+(\.\w+)*)$/) { + $extension{$1} = $string unless exists $extension{$1}; + $mime2ext{$string} = [] if !defined($mime2ext{$string}); + push @{$mime2ext{$string}}, $1; + } else { unshift @globs, [$glob, _glob_to_regexp($glob), $string] } + } + close GLOB || croak "Could not open file '$file' for reading" ; } sub _glob_to_regexp { - my $glob = shift; - $glob =~ s/\./\\./g; - $glob =~ s/([?*])/.$1/g; - $glob =~ s/([^\w\/\\\.\?\*\[\]])/\\$1/g; - qr/^$glob$/; + my $glob = shift; + $glob =~ s/\./\\./g; + $glob =~ s/([?*])/.$1/g; + $glob =~ s/([^\w\/\\\.\?\*\[\]])/\\$1/g; + qr/^$glob$/; +} + +sub has_mimeinfo_database { + rehash() if (!$_hashed); + return $_has_mimeinfo_database; } sub extensions { - my $mimet = mimetype_canon(pop @_); - rehash() unless $_hashed; + my $mimet = mimetype_canon(pop @_); + rehash() unless $_hashed; my $ref = $mime2ext{$mimet} if exists $mime2ext{$mimet}; - return $ref ? @{$ref} : undef if wantarray; + return $ref ? @{$ref} : undef if wantarray; return $ref ? @{$ref}[0] : ''; } sub describe { - shift if ref $_[0]; - my ($mt, $lang) = @_; - croak 'subroutine "describe" needs a mimetype as argument' unless $mt; - $mt = mimetype_canon($mt); - $lang = $LANG unless defined $lang; - my $att = $lang ? qq{xml:lang="$lang"} : ''; - my $desc; - my @descfiles = @DIRS - ? ( grep {-e $_ && -r $_} map "$_/$mt.xml", @DIRS ) - : ( reverse data_files('mime', split '/', "$mt.xml") ) ; - for my $file (@descfiles) { - $desc = ''; # if a file was found, return at least empty string - open XML, '<', $file || croak "Could not open file '$file' for reading"; - binmode XML, ':utf8' unless $] < 5.008; - while (<XML>) { - next unless m!<comment\s*$att>(.*?)</comment>!; - $desc = $1; - last; - } - close XML || croak "Could not open file '$file' for reading"; - last if $desc; - } - return $desc; + shift if ref $_[0]; + my ($mt, $lang) = @_; + croak 'subroutine "describe" needs a mimetype as argument' unless $mt; + $mt = mimetype_canon($mt); + $lang = $LANG unless defined $lang; + my $att = $lang ? qq{xml:lang="$lang"} : ''; + my $desc; + my @descfiles = @DIRS + ? ( grep {-e $_ && -r $_} map "$_/$mt.xml", @DIRS ) + : ( reverse data_files('mime', split '/', "$mt.xml") ) ; + for my $file (@descfiles) { + $desc = ''; # if a file was found, return at least empty string + open XML, '<', $file || croak "Could not open file '$file' for reading"; + binmode XML, ':utf8' unless $] < 5.008; + while (<XML>) { + next unless m!<comment\s*$att>(.*?)</comment>!; + $desc = $1; + last; + } + close XML || croak "Could not open file '$file' for reading"; + last if $desc; + } + return $desc; } sub mimetype_canon { - my $mimet = pop; - croak 'mimetype_canon needs argument' unless defined $mimet; - rehash_aliases() unless $_hashed_aliases; - return exists($aliases{$mimet}) ? $aliases{$mimet} : $mimet; + my $mimet = pop; + croak 'mimetype_canon needs argument' unless defined $mimet; + rehash_aliases() unless $_hashed_aliases; + return exists($aliases{$mimet}) ? $aliases{$mimet} : $mimet; } sub rehash_aliases { - %aliases = _read_map_files('aliases'); - $_hashed_aliases++; + %aliases = _read_map_files('aliases'); + $_hashed_aliases++; } sub _read_map_files { - my ($name, $list) = @_; - my @files = @DIRS - ? ( grep {-e $_ && -r $_} map "$_/$name", @DIRS ) - : ( reverse data_files("mime/$name") ); - my (@done, %map); - for my $file (@files) { - next if grep {$_ eq $file} @done; - open MAP, '<', $file || croak "Could not open file '$file' for reading"; - binmode MAP, ':utf8' unless $] < 5.008; - while (my $line = <MAP>) { - next unless $line =~ m/\S/; # skip empty lines - next if $line =~ m/^\s*#/; # skip comment lines - chomp $line; - my ($k, $v) = split m/\s+/, $line, 2; - if ($list) { - $map{$k} = [] unless $map{$k}; - push @{$map{$k}}, $v; - } - else { $map{$k} = $v } - } - close MAP; - push @done, $file; - } - return %map; + my ($name, $list) = @_; + my @files = @DIRS + ? ( grep {-e $_ && -r $_} map "$_/$name", @DIRS ) + : ( reverse data_files("mime/$name") ); + my (@done, %map); + for my $file (@files) { + next if grep {$_ eq $file} @done; + open MAP, '<', $file || croak "Could not open file '$file' for reading"; + binmode MAP, ':utf8' unless $] < 5.008; + while (my $line = <MAP>) { + next unless $line =~ m/\S/; # skip empty lines + next if $line =~ m/^\s*#/; # skip comment lines + chomp $line; + my ($k, $v) = split m/\s+/, $line, 2; + if ($list) { + $map{$k} = [] unless $map{$k}; + push @{$map{$k}}, $v; + } + else { $map{$k} = $v } + } + close MAP; + push @done, $file; + } + return %map; } sub mimetype_isa { - my $parent = pop || croak 'mimetype_isa needs argument'; - my $mimet = pop; - if (ref $mimet or ! defined $mimet) { - $mimet = mimetype_canon($parent); - undef $parent; - } - else { - $mimet = mimetype_canon($mimet); - $parent = mimetype_canon($parent); - } - rehash_subclasses() unless $_hashed_subclasses; - - my @subc; - push @subc, 'inode/directory' if $mimet eq 'inode/mount-point'; - push @subc, @{$subclasses{$mimet}} if exists $subclasses{$mimet}; - push @subc, 'text/plain' if $mimet =~ m#^text/#; - push @subc, 'application/octet-stream' unless $mimet =~ m#^inode/#; - - return $parent ? scalar(grep {$_ eq $parent} @subc) : @subc; + my $parent = pop || croak 'mimetype_isa needs argument'; + my $mimet = pop; + if (ref $mimet or ! defined $mimet) { + $mimet = mimetype_canon($parent); + undef $parent; + } + else { + $mimet = mimetype_canon($mimet); + $parent = mimetype_canon($parent); + } + rehash_subclasses() unless $_hashed_subclasses; + + my @subc; + push @subc, 'inode/directory' if $mimet eq 'inode/mount-point'; + push @subc, @{$subclasses{$mimet}} if exists $subclasses{$mimet}; + push @subc, 'text/plain' if $mimet =~ m#^text/#; + push @subc, 'application/octet-stream' unless $mimet =~ m#^inode/#; + + return $parent ? scalar(grep {$_ eq $parent} @subc) : @subc; } sub rehash_subclasses { - %subclasses = _read_map_files('subclasses', 'LIST'); - $_hashed_subclasses++; + %subclasses = _read_map_files('subclasses', 'LIST'); + $_hashed_subclasses++; } 1; @@ -409,6 +417,15 @@ the first one. This method checks the subclasses table and applies a few rules for implicit subclasses. +=item C<has_mimeinfo_database()> + +Check if there are mimeinfo database files available; returns 1 on success. +If you don't have the shared-mime-info package installed or not in the PATH or +C<@File::MimeInfo::DIRS> does not contain database directories, you will not get +the successful reply. + +New in version 0.30. + =item C<rehash()> Rehash the data files. Glob information is preparsed when this method is called. @@ -454,6 +471,14 @@ in a straightforward manner only utf8 is supported (because the spec recommends This module does not yet check extended attributes for a mimetype. Patches for this are very welcome. +This module uses the FreeDesktop.org shared mime info database. On your desktop +linux this is typically pre-installed so it's not a problem. On your server +you can install the shared-mime-info package via apt or dnf or apk or whatnot. + +To install on macOS, you can install it like this: + + brew install shared-mime-info + =head1 AUTHOR Jaap Karssenberg E<lt>pardus@cpan.orgE<gt> diff --git a/lib/File/MimeInfo/Applications.pm b/lib/File/MimeInfo/Applications.pm index e6142f6..35279a8 100644 --- a/lib/File/MimeInfo/Applications.pm +++ b/lib/File/MimeInfo/Applications.pm @@ -1,6 +1,7 @@ package File::MimeInfo::Applications; use strict; +use warnings; use Carp; use File::Spec; use File::BaseDir qw/config_home config_dirs data_home data_dirs data_files/; @@ -8,12 +9,12 @@ use File::MimeInfo qw/mimetype_canon mimetype_isa/; use File::DesktopEntry; require Exporter; -our $VERSION = '0.29'; +our $VERSION = '0.30'; our @ISA = qw(Exporter); our @EXPORT = qw( - mime_applications mime_applications_all - mime_applications_set_default mime_applications_set_custom + mime_applications mime_applications_all + mime_applications_set_default mime_applications_set_custom ); print STDERR << 'EOT' unless data_files(qw/applications mimeinfo.cache/); @@ -25,179 +26,179 @@ http://freedesktop.org/wiki/Software/desktop-file-utils/ EOT sub mime_applications { - croak "usage: mime_applications(MIMETYPE)" unless @_ == 1; - my $mime = mimetype_canon(shift @_); - local $Carp::CarpLevel = $Carp::CarpLevel + 1; - return wantarray ? (_default($mime), _others($mime)) : _default($mime); + croak "usage: mime_applications(MIMETYPE)" unless @_ == 1; + my $mime = mimetype_canon(shift @_); + local $Carp::CarpLevel = $Carp::CarpLevel + 1; + return wantarray ? (_default($mime), _others($mime)) : _default($mime); } sub mime_applications_all { - croak "usage: mime_applications(MIMETYPE)" unless @_ == 1; - my $mime = shift; - return mime_applications($mime), - grep defined($_), map mime_applications($_), mimetype_isa($mime); + croak "usage: mime_applications(MIMETYPE)" unless @_ == 1; + my $mime = shift; + return mime_applications($mime), + grep defined($_), map mime_applications($_), mimetype_isa($mime); } sub mime_applications_set_default { - croak "usage: mime_applications_set_default(MIMETYPE, APPLICATION)" - unless @_ == 2; - my ($mimetype, $desktop_file) = @_; - (undef, undef, $desktop_file) = - File::Spec->splitpath($desktop_file->{file}) - if ref $desktop_file; - croak "missing desktop entry filename for application" - unless length $desktop_file; - $desktop_file .= '.desktop' unless $desktop_file =~ /\.desktop$/; - _write_list($mimetype, $desktop_file); + croak "usage: mime_applications_set_default(MIMETYPE, APPLICATION)" + unless @_ == 2; + my ($mimetype, $desktop_file) = @_; + (undef, undef, $desktop_file) = + File::Spec->splitpath($desktop_file->{file}) + if ref $desktop_file; + croak "missing desktop entry filename for application" + unless length $desktop_file; + $desktop_file .= '.desktop' unless $desktop_file =~ /\.desktop$/; + _write_list($mimetype, $desktop_file); } sub mime_applications_set_custom { - croak "usage: mime_applications_set_custom(MIMETYPE, COMMAND)" - unless @_ == 2; - my ($mimetype, $command) = @_; - $command =~ /(\w+)/; - my $word = $1 or croak "COMMAND does not contain a word !?"; - - # Algorithm to generate name copied from other implementations - my $i = 1; - my $desktop_file = - data_home('applications', $word.'-usercreated-'.$i.'.desktop'); - while (-e $desktop_file) { - $i++; - $desktop_file = - data_home('applications', $word.'-usercreated-'.$i.'.desktop'); - } - - my $object = File::DesktopEntry->new(); - $object->set( - Type => 'Application', - Name => $word, - NoDisplay => 'true', - Exec => $command, - ); - my (undef, undef, $df) = File::Spec->splitpath($desktop_file); - _write_list($mimetype, $df); # creates dir if needed - $object->write($desktop_file); - return $object; + croak "usage: mime_applications_set_custom(MIMETYPE, COMMAND)" + unless @_ == 2; + my ($mimetype, $command) = @_; + $command =~ /(\w+)/; + my $word = $1 or croak "COMMAND does not contain a word !?"; + + # Algorithm to generate name copied from other implementations + my $i = 1; + my $desktop_file = + data_home('applications', $word.'-usercreated-'.$i.'.desktop'); + while (-e $desktop_file) { + $i++; + $desktop_file = + data_home('applications', $word.'-usercreated-'.$i.'.desktop'); + } + + my $object = File::DesktopEntry->new(); + $object->set( + Type => 'Application', + Name => $word, + NoDisplay => 'true', + Exec => $command, + ); + my (undef, undef, $df) = File::Spec->splitpath($desktop_file); + _write_list($mimetype, $df); # creates dir if needed + $object->write($desktop_file); + return $object; } sub _default { - my $mimetype = shift; - - my $user = config_home(qw/mimeapps.list/); - my $system = config_dirs(qw/mimeapps.list/); - my $deprecated = data_home(qw/applications mimeapps.list/); - my $distro = data_dirs(qw/applications mimeapps.list/); - my $legacy = data_home(qw/applications defaults.list/); - - unless ( ( -f $user - || -f $system - || -f $deprecated - || -f $distro - || -f $legacy ) - && -r _ ) { - return undef; - } - - $Carp::CarpLevel++; - my @list = - _read_list($mimetype, $user, $system, $deprecated, $distro, $legacy); - my $desktop_file = _find_file(reverse @list); - $Carp::CarpLevel--; - - return $desktop_file; + my $mimetype = shift; + + my $user = config_home(qw/mimeapps.list/); + my $system = config_dirs(qw/mimeapps.list/); + my $deprecated = data_home(qw/applications mimeapps.list/); + my $distro = data_dirs(qw/applications mimeapps.list/); + my $legacy = data_home(qw/applications defaults.list/); + + unless ( ( -f $user + || ($system && -f $system) + || ($deprecated && -f $deprecated) + || ($distro && -f $distro) + || ($legacy && -f $legacy) ) + && -r _ ) { + return undef; + } + + $Carp::CarpLevel++; + my @list = + _read_list($mimetype, $user, $system, $deprecated, $distro, $legacy); + my $desktop_file = _find_file(reverse @list); + $Carp::CarpLevel--; + + return $desktop_file; } sub _others { - my $mimetype = shift; - - $Carp::CarpLevel++; - my (@list, @done); - for my $dir (data_dirs('applications')) { - my $cache = File::Spec->catfile($dir, 'mimeinfo.cache'); - next if grep {$_ eq $cache} @done; - push @done, $cache; - next unless -f $cache and -r _; - for (_read_list($mimetype, $cache)) { - my $file = File::Spec->catfile($dir, $_); - next unless -f $file and -r _; - push @list, File::DesktopEntry->new($file); - } - } - $Carp::CarpLevel--; - - return @list; + my $mimetype = shift; + + $Carp::CarpLevel++; + my (@list, @done); + for my $dir (data_dirs('applications')) { + my $cache = File::Spec->catfile($dir, 'mimeinfo.cache'); + next if grep {$_ eq $cache} @done; + push @done, $cache; + next unless -f $cache and -r _; + for (_read_list($mimetype, $cache)) { + my $file = File::Spec->catfile($dir, $_); + next unless -f $file and -r _; + push @list, File::DesktopEntry->new($file); + } + } + $Carp::CarpLevel--; + + return @list; } sub _read_list { # read list with "mime/type=foo.desktop;bar.desktop" format - my $mimetype = shift; - - my @list; - my $succeeded; - - for my $file (@_) { - if (open LIST, '<', $file) { - $succeeded = 1; - while (<LIST>) { - /^\Q$mimetype\E=(.*)$/ or next; - push @list, grep defined($_), split ';', $1; - } - close LIST; - } - } - - unless ($succeeded) { - croak "Could not read any defaults, tried:\n" . join("\t\n", @_); - } - - return @list; + my $mimetype = shift; + + my @list; + my $succeeded; + + for my $file (@_) { + if (open LIST, '<', $file) { + $succeeded = 1; + while (<LIST>) { + /^\Q$mimetype\E=(.*)$/ or next; + push @list, grep defined($_), split ';', $1; + } + close LIST; + } + } + + unless ($succeeded) { + croak "Could not read any defaults, tried:\n" . join("\t\n", @_); + } + + return @list; } sub _write_list { - my ($mimetype, $desktop_file) = @_; - my $file = config_home(qw/mimeapps.list/); - my $text; - if (-f $file) { - open LIST, '<', $file or croak "Could not read file: $file"; - while (<LIST>) { - $text .= $_ unless /^\Q$mimetype\E=/; - } - close LIST; - $text =~ s/[\n\r]?$/\n/; # just to be sure - } - else { - _mkdir($file); - $text = "[Default Applications]\n"; - } - - open LIST, '>', $file or croak "Could not write file: $file"; - print LIST $text; - print LIST "$mimetype=$desktop_file;\n"; - close LIST or croak "Could not write file: $file"; + my ($mimetype, $desktop_file) = @_; + my $file = config_home(qw/mimeapps.list/); + my $text; + if (-f $file) { + open LIST, '<', $file or croak "Could not read file: $file"; + while (<LIST>) { + $text .= $_ unless /^\Q$mimetype\E=/; + } + close LIST; + $text =~ s/[\n\r]?$/\n/; # just to be sure + } + else { + _mkdir($file); + $text = "[Default Applications]\n"; + } + + open LIST, '>', $file or croak "Could not write file: $file"; + print LIST $text; + print LIST "$mimetype=$desktop_file;\n"; + close LIST or croak "Could not write file: $file"; } sub _find_file { - my @list = shift; - for (@list) { - my $file = data_files('applications', $_); - return File::DesktopEntry->new($file) if $file; - } - return undef; + my @list = shift; + for (@list) { + my $file = data_files('applications', $_); + return File::DesktopEntry->new($file) if $file; + } + return undef; } sub _mkdir { - my $dir = shift; - return if -d $dir; - - my ($vol, $dirs, undef) = File::Spec->splitpath($dir); - my @dirs = File::Spec->splitdir($dirs); - my $path = File::Spec->catpath($vol, shift @dirs); - while (@dirs) { - mkdir $path; # fails silently - $path = File::Spec->catdir($path, shift @dirs); - } - - die "Could not create dir: $path\n" unless -d $path; + my $dir = shift; + return if -d $dir; + + my ($vol, $dirs, undef) = File::Spec->splitpath($dir); + my @dirs = File::Spec->splitdir($dirs); + my $path = File::Spec->catpath($vol, shift @dirs); + while (@dirs) { + mkdir $path; # fails silently + $path = File::Spec->catdir($path, shift @dirs); + } + + die "Could not create dir: $path\n" unless -d $path; } 1; @@ -215,16 +216,16 @@ File::MimeInfo::Applications - Find programs to open a file by mimetype my $file = '/foo/bar'; my $mimetype = mimetype($file) - || die "Could not find mimetype for $file\n"; + || die "Could not find mimetype for $file\n"; my ($default, @other) = mime_applications($mimetype); if (defined $default) { - $default->system($file) + $default->system($file) } else { - # prompt user with choice from @others - # ... + # prompt user with choice from @others + # ... } =head1 DESCRIPTION diff --git a/lib/File/MimeInfo/Magic.pm b/lib/File/MimeInfo/Magic.pm index 3e96f09..23cac41 100644 --- a/lib/File/MimeInfo/Magic.pm +++ b/lib/File/MimeInfo/Magic.pm @@ -8,16 +8,16 @@ require File::MimeInfo; require Exporter; BEGIN { - no strict "refs"; - for (qw/extensions describe globs inodetype default/) { - *{$_} = \&{"File::MimeInfo::$_"}; - } + no strict "refs"; + for (qw/extensions describe globs inodetype default/) { + *{$_} = \&{"File::MimeInfo::$_"}; + } } our @ISA = qw(Exporter File::MimeInfo); our @EXPORT = qw(mimetype); our @EXPORT_OK = qw(extensions describe globs inodetype magic); -our $VERSION = '0.29'; +our $VERSION = '0.30'; our $DEBUG; our $_hashed = 0; @@ -29,222 +29,222 @@ our (@magic_80, @magic); # filehandle in order to do magic mimetyping sub mimetype { - my $file = pop; - croak 'subroutine "mimetype" needs a filename as argument' unless defined $file; + my $file = pop; + croak 'subroutine "mimetype" needs a filename as argument' unless defined $file; - return magic($file) || default($file) if ref $file; - return &File::MimeInfo::mimetype($file) unless -s $file and -r _; + return magic($file) || default($file) if ref $file; + return &File::MimeInfo::mimetype($file) unless -s $file and -r _; - my ($mimet, $fh); - return $mimet if $mimet = inodetype($file); + my ($mimet, $fh); + return $mimet if $mimet = inodetype($file); - ($mimet, $fh) = _magic($file, \@magic_80); # high priority rules - return $mimet if $mimet; + ($mimet, $fh) = _magic($file, \@magic_80); # high priority rules + return $mimet if $mimet; - return $mimet if $mimet = globs($file); + return $mimet if $mimet = globs($file); - ($mimet, $fh) = _magic($fh, \@magic); # lower priority rules - close $fh if ref $fh; + ($mimet, $fh) = _magic($fh, \@magic); # lower priority rules + close $fh if ref $fh; - return $mimet if $mimet; - return default($file); + return $mimet if $mimet; + return default($file); } sub magic { - my $file = pop; - croak 'subroutine "magic" needs a filename as argument' unless defined $file; - return undef unless ref($file) || -s $file; - print STDERR "> Checking all magic rules\n" if $DEBUG; + my $file = pop; + croak 'subroutine "magic" needs a filename as argument' unless defined $file; + return undef unless ref($file) || -s $file; + print STDERR "> Checking all magic rules\n" if $DEBUG; - my ($mimet, $fh) = _magic($file, \@magic_80, \@magic); - close $fh unless ref $file; + my ($mimet, $fh) = _magic($file, \@magic_80, \@magic); + close $fh unless ref $file; - return $mimet; + return $mimet; } sub _magic { - my ($file, @rules) = @_; - _rehash() unless $_hashed; - - my $fh; - unless (ref $file) { - open $fh, '<', $file or return undef; - binmode $fh; - } - else { $fh = $file } - - for my $type (map @$_, @rules) { - for (2..$#$type) { - next unless _check_rule($$type[$_], $fh, 0); - close $fh unless ref $file; - return ($$type[1], $fh); - } - } - return (undef, $fh); + my ($file, @rules) = @_; + _rehash() unless $_hashed; + + my $fh; + unless (ref $file) { + open $fh, '<', $file or return undef; + binmode $fh; + } + else { $fh = $file } + + for my $type (map @$_, @rules) { + for (2..$#$type) { + next unless _check_rule($$type[$_], $fh, 0); + close $fh unless ref $file; + return ($$type[1], $fh); + } + } + return (undef, $fh); } sub _check_rule { - my ($ref, $fh, $lev) = @_; - my $line; - - # Read - if (ref $fh eq 'GLOB') { - seek($fh, $$ref[0], SEEK_SET); # seek offset - read($fh, $line, $$ref[1]); # read max length - } - else { # allowing for IO::Something - $fh->seek($$ref[0], SEEK_SET); # seek offset - $fh->read($line, $$ref[1]); # read max length - } - - # Match regex - $line = unpack 'b*', $line if $$ref[2]; # unpack to bits if using mask - return undef unless $line =~ $$ref[3]; # match regex - print STDERR '>', '>'x$lev, ' Value "', _escape_bytes($2), - '" at offset ', $$ref[1]+length($1), - " matches at $$ref[4]\n" - if $DEBUG; - return 1 unless $#$ref > 4; - - # Check nested rules and recurs - for (5..$#$ref) { - return 1 if _check_rule($$ref[$_], $fh, $lev+1); - } - print STDERR "> Failed nested rules\n" if $DEBUG && ! $lev; - return 0; + my ($ref, $fh, $lev) = @_; + my $line; + + # Read + if (ref $fh eq 'GLOB') { + seek($fh, $$ref[0], SEEK_SET); # seek offset + read($fh, $line, $$ref[1]); # read max length + } + else { # allowing for IO::Something + $fh->seek($$ref[0], SEEK_SET); # seek offset + $fh->read($line, $$ref[1]); # read max length + } + + # Match regex + $line = unpack 'b*', $line if $$ref[2]; # unpack to bits if using mask + return undef unless $line =~ $$ref[3]; # match regex + print STDERR '>', '>'x$lev, ' Value "', _escape_bytes($2), + '" at offset ', $$ref[1]+length($1), + " matches at $$ref[4]\n" + if $DEBUG; + return 1 unless $#$ref > 4; + + # Check nested rules and recurs + for (5..$#$ref) { + return 1 if _check_rule($$ref[$_], $fh, $lev+1); + } + print STDERR "> Failed nested rules\n" if $DEBUG && ! $lev; + return 0; } sub rehash { - &File::MimeInfo::rehash(); - &_rehash(); - #use Data::Dumper; - #print Dumper \@magic_80, \@magic; + &File::MimeInfo::rehash(); + &_rehash(); + #use Data::Dumper; + #print Dumper \@magic_80, \@magic; } sub _rehash { - local $_; # limit scope of $_ ... :S - ($max_buffer, @magic_80, @magic) = (32); # clear data - my @magicfiles = @File::MimeInfo::DIRS - ? ( grep {-e $_ && -r $_} - map "$_/magic", @File::MimeInfo::DIRS ) - : ( reverse data_files('mime/magic') ) ; - my @done; - for my $file (@magicfiles) { - next if grep {$file eq $_} @done; - _hash_magic($file); - push @done, $file; - } - @magic = sort {$$b[0] <=> $$a[0]} @magic; - while ($magic[0][0] >= 80) { - push @magic_80, shift @magic; - } - $_hashed = 1; + local $_; # limit scope of $_ ... :S + ($max_buffer, @magic_80, @magic) = (32); # clear data + my @magicfiles = @File::MimeInfo::DIRS + ? ( grep {-e $_ && -r $_} + map "$_/magic", @File::MimeInfo::DIRS ) + : ( reverse data_files('mime/magic') ) ; + my @done; + for my $file (@magicfiles) { + next if grep {$file eq $_} @done; + _hash_magic($file); + push @done, $file; + } + @magic = sort {$$b[0] <=> $$a[0]} @magic; + while ($magic[0][0] >= 80) { + push @magic_80, shift @magic; + } + $_hashed = 1; } sub _hash_magic { - my $file = shift; - - open MAGIC, '<', $file - || croak "Could not open file '$file' for reading"; - binmode MAGIC; - <MAGIC> eq "MIME-Magic\x00\n" - or carp "Magic file '$file' doesn't seem to be a magic file"; - my $line = 1; - while (<MAGIC>) { - $line++; - - if (/^\[(\d+):(.*?)\]\n$/) { - push @magic, [$1,$2]; - next; - } - - s/^(\d*)>(\d+)=(.{2})//s - || warn "$file line $line skipped\n" && next; - my ($i, $o, $l) = ($1, $2, unpack 'n', $3); - # indent, offset, value length - while (length($_) <= $l) { - $_ .= <MAGIC>; - $line++; - } - - my $v = substr $_, 0, $l, ''; # value - - /^(?:&(.{$l}))?(?:~(\d+))?(?:\+(\d+))?\n$/s - || warn "$file line $line skipped\n" && next; - my ($m, $w, $r) = ($1, $2 || 1, $3 || 1); - # mask, word size, range - my $mdef = defined $m; - - # possible big endian to little endian conversion - # as a bonus perl also takes care of weird endian cases - if ( $w != 1 ) { - my ( $utpl, $ptpl ); - if ( 2 == $w ) { - $v = pack 'S', unpack 'n', $v; - $m = pack 'S', unpack 'n', $m if $mdef; - } - elsif ( 4 == $w ) { - $v = pack 'L', unpack 'N', $v; - $m = pack 'L', unpack 'N', $m if $mdef; - } - else { - warn "Unsupported word size: $w octets ". - " at $file line $line\n" - } - } - - my $end = $o + $l + $r - 1; - $max_buffer = $end if $max_buffer < $end; - my $ref = $i ? _find_branch($i) : $magic[-1]; - $r--; # 1-based => 0-based range for regex - $r *= 8 if $mdef; # bytes => bits for matching a mask - my $reg = '^' - . ( $r ? "(.{0,$r}?)" : '()' ) - . ( $mdef ? '('. _mask_regex($v, $m) .')' - : '('. quotemeta($v) .')' ) ; - push @$ref, [ - $o, $end, # offset, offset+length+range - $mdef, # boolean for mask - qr/$reg/sm, # the regex to match - undef # debug data - ]; - $$ref[-1][-1] = "$file line $line" if $DEBUG; - } - close MAGIC; + my $file = shift; + + open MAGIC, '<', $file + || croak "Could not open file '$file' for reading"; + binmode MAGIC; + <MAGIC> eq "MIME-Magic\x00\n" + or carp "Magic file '$file' doesn't seem to be a magic file"; + my $line = 1; + while (<MAGIC>) { + $line++; + + if (/^\[(\d+):(.*?)\]\n$/) { + push @magic, [$1,$2]; + next; + } + + s/^(\d*)>(\d+)=(.{2})//s + || warn "$file line $line skipped\n" && next; + my ($i, $o, $l) = ($1, $2, unpack 'n', $3); + # indent, offset, value length + while (length($_) <= $l) { + $_ .= <MAGIC>; + $line++; + } + + my $v = substr $_, 0, $l, ''; # value + + /^(?:&(.{$l}))?(?:~(\d+))?(?:\+(\d+))?\n$/s + || warn "$file line $line skipped\n" && next; + my ($m, $w, $r) = ($1, $2 || 1, $3 || 1); + # mask, word size, range + my $mdef = defined $m; + + # possible big endian to little endian conversion + # as a bonus perl also takes care of weird endian cases + if ( $w != 1 ) { + my ( $utpl, $ptpl ); + if ( 2 == $w ) { + $v = pack 'S', unpack 'n', $v; + $m = pack 'S', unpack 'n', $m if $mdef; + } + elsif ( 4 == $w ) { + $v = pack 'L', unpack 'N', $v; + $m = pack 'L', unpack 'N', $m if $mdef; + } + else { + warn "Unsupported word size: $w octets ". + " at $file line $line\n" + } + } + + my $end = $o + $l + $r - 1; + $max_buffer = $end if $max_buffer < $end; + my $ref = $i ? _find_branch($i) : $magic[-1]; + $r--; # 1-based => 0-based range for regex + $r *= 8 if $mdef; # bytes => bits for matching a mask + my $reg = '^' + . ( $r ? "(.{0,$r}?)" : '()' ) + . ( $mdef ? '('. _mask_regex($v, $m) .')' + : '('. quotemeta($v) .')' ) ; + push @$ref, [ + $o, $end, # offset, offset+length+range + $mdef, # boolean for mask + qr/$reg/sm, # the regex to match + undef # debug data + ]; + $$ref[-1][-1] = "$file line $line" if $DEBUG; + } + close MAGIC; } sub _find_branch { # finds last branch of tree of rules - my $i = shift; - my $ref = $magic[-1]; - for (1..$i) { $ref = $$ref[-1] } - return $ref; + my $i = shift; + my $ref = $magic[-1]; + for (1..$i) { $ref = $$ref[-1] } + return $ref; } sub _mask_regex { # build regex based on mask - my ($v, $m) = @_; - my @v = split '', unpack "b*", $v; - my @m = split '', unpack "b*", $m; - my $re = ''; - for (0 .. $#m) { - $re .= $m[$_] ? $v[$_] : '.' ; - # If $mask = 1 than ($input && $mask) will be same as $input - # If $mask = 0 than ($input && $mask) is always 0 - # But $mask = 0 only makes sense if $value = 0 - # So if $mask = 0 we ignore that bit of $input - } - return $re; + my ($v, $m) = @_; + my @v = split '', unpack "b*", $v; + my @m = split '', unpack "b*", $m; + my $re = ''; + for (0 .. $#m) { + $re .= $m[$_] ? $v[$_] : '.' ; + # If $mask = 1 than ($input && $mask) will be same as $input + # If $mask = 0 than ($input && $mask) is always 0 + # But $mask = 0 only makes sense if $value = 0 + # So if $mask = 0 we ignore that bit of $input + } + return $re; } sub _escape_bytes { # used for debug output - my $string = shift; - if ($string =~ /[\x00-\x1F\x7F]/) { - $string = join '', map { - my $o = ord($_); - ($o < 32) ? '^' . chr($o + 64) : - ($o == 127) ? '^?' : $_ ; - } split '', $string; - } - return $string; + my $string = shift; + if ($string =~ /[\x00-\x1F\x7F]/) { + $string = join '', map { + my $o = ord($_); + ($o < 32) ? '^' . chr($o + 64) : + ($o == 127) ? '^?' : $_ ; + } split '', $string; + } + return $string; } 1; @@ -257,8 +257,8 @@ File::MimeInfo::Magic - Determine file type with magic =head1 SYNOPSIS - use File::MimeInfo::Magic; - my $mime_type = mimetype($file); + use File::MimeInfo::Magic; + my $mime_type = mimetype($file); =head1 DESCRIPTION diff --git a/lib/File/MimeInfo/Rox.pm b/lib/File/MimeInfo/Rox.pm index 17b0a17..22e06af 100644 --- a/lib/File/MimeInfo/Rox.pm +++ b/lib/File/MimeInfo/Rox.pm @@ -1,6 +1,7 @@ package File::MimeInfo::Rox; use strict; +use warnings; use Carp; use File::BaseDir qw/config_home data_dirs/; use File::Spec; @@ -10,74 +11,74 @@ our @ISA = qw(Exporter); our @EXPORT = qw(mime_exec mime_system); our @EXPORT_OK = qw(suggest_script_name); our %EXPORT_TAGS = (magic => \@EXPORT); -our $VERSION = '0.29'; +our $VERSION = '0.30'; our @choicespath = ( - config_home('rox.sourceforge.net'), - File::Spec->catdir($ENV{HOME}, 'Choices'), - data_dirs('Choices'), + config_home('rox.sourceforge.net'), + File::Spec->catdir($ENV{HOME}, 'Choices'), + data_dirs('Choices'), ); our ($DEBUG); sub import { - my $parent = (grep {$_ eq q/:magic/} @_) - ? q/File::MimeInfo::Magic/ - : q/File::MimeInfo/; - eval "use $parent"; - die $@ if $@; - goto \&Exporter::import; + my $parent = (grep {$_ eq q/:magic/} @_) + ? q/File::MimeInfo::Magic/ + : q/File::MimeInfo/; + eval "use $parent"; + die $@ if $@; + goto \&Exporter::import; } sub mime_system { _do_mime('system', @_) } sub mime_exec { _do_mime('exec', @_) } sub _do_mime { - my ($act, $file, $mimet) = (shift, shift, shift); + my ($act, $file, $mimet) = (shift, shift, shift); - $mimet ||= mimetype($file); - return undef unless $mimet; - print "Using mimetype: $mimet\n" if $DEBUG; + $mimet ||= mimetype($file); + return undef unless $mimet; + print "Using mimetype: $mimet\n" if $DEBUG; - my $script = _locate_script($mimet); - return undef unless $script; + my $script = _locate_script($mimet); + return undef unless $script; - print "Going to $act: $script $file\n" if $DEBUG; - ($act eq 'exec') - ? exec($script, $file, @_) - : (system($script, $file, @_) == 0) - or croak "couldn't $act: $script $file"; - 42; + print "Going to $act: $script $file\n" if $DEBUG; + ($act eq 'exec') + ? exec($script, $file, @_) + : (system($script, $file, @_) == 0) + or croak "couldn't $act: $script $file"; + 42; } sub _locate_script { - my $mime = shift; - $mime =~ /^(\w+)/; - my $media = $1; - $mime =~ s#/#_#; - my @p = $ENV{CHOICESPATH} - ? split(/:/, $ENV{CHOICESPATH}) - : (@choicespath); - my $script; - for ( - map("$_/MIME-types/$mime", @p), - map("$_/MIME-types/$media", @p) - ) { - print "looking for: $_\n" if $DEBUG; - next unless -e $_; - $script = $_; - last; - } - return undef unless $script; - $script = "$script/AppRun" if -d $script; - return -f $script ? $script : undef; + my $mime = shift; + $mime =~ /^(\w+)/; + my $media = $1; + $mime =~ s#/#_#; + my @p = $ENV{CHOICESPATH} + ? split(/:/, $ENV{CHOICESPATH}) + : (@choicespath); + my $script; + for ( + map("$_/MIME-types/$mime", @p), + map("$_/MIME-types/$media", @p) + ) { + print "looking for: $_\n" if $DEBUG; + next unless -e $_; + $script = $_; + last; + } + return undef unless $script; + $script = "$script/AppRun" if -d $script; + return -f $script ? $script : undef; } sub suggest_script_name { - my $m = pop; - $m =~ s#/#_#; - my @p = $ENV{CHOICESPATH} - ? split(/:/, $ENV{CHOICESPATH}) - : (@choicespath); - return "$p[0]/MIME-types", $m; + my $m = pop; + $m =~ s#/#_#; + my @p = $ENV{CHOICESPATH} + ? split(/:/, $ENV{CHOICESPATH}) + : (@choicespath); + return "$p[0]/MIME-types", $m; } 1; @@ -98,9 +99,9 @@ File::MimeInfo::Rox - Open files by mimetype "Rox style" # more verbose version my $mt = mimetype($somefile) - || die "Could not find mimetype for $somefile\n"; + || die "Could not find mimetype for $somefile\n"; mime_system($somefile, $mt) - || die "No program to open $somefile available\n"; + || die "No program to open $somefile available\n"; =head1 DESCRIPTION @@ -1,71 +1,75 @@ #!/usr/bin/perl use strict; -our $VERSION = '0.29'; +use warnings; +our $VERSION = '0.30'; $|++; # ########## # # Parse ARGV # # ########## # +use Encode::Locale; +use Encode; +@ARGV = map { decode(locale => $_, 1) } @ARGV; + my %args = (); my %opts = ( - # name => [char, expect_arg_bit ] - 'help' => ['h'], - 'usage' => ['u'], - 'version' => ['v'], - 'stdin' => [''], - 'dereference' => ['L'], - 'debug' => ['D'], - 'database' => ['', 1], - 'magic-only' => ['M'], - 'ask' => ['a'], - 'ask-default' => ['d'], - 'no-ask' => ['n'], + 'help' => ['h'], + 'usage' => ['u'], + 'version' => ['v'], + 'stdin' => [''], + 'dereference' => ['L'], + 'debug' => ['D'], + 'database' => ['', 1], + 'magic-only' => ['M'], + 'ask' => ['a'], + 'ask-default' => ['d'], + 'no-ask' => ['n'], ); while ((@ARGV) && ($ARGV[0] =~ /^-/)) { - my $opt = shift @ARGV; - if ($opt =~ /^--?$/) { - last; - } - elsif ($opt =~ s/^--([\w-]+)(?:=(.*))?/$1/) { - if (exists $opts{$opt}) { - if ($opts{$opt}[1]) { - my $arg = $2 || shift @ARGV; - complain('--'.$opt, 2) unless defined $arg; - $args{$opt} .= ( $args{$opt} ? ' ' : '' ) . $arg; - } - else { $args{$opt}++ } - } - else { complain('--'.$opt) } - } - elsif ($opt =~ s/^-(?!-)//) { - foreach my $o (split //, $opt) { - my ($key) = grep { $opts{$_}[0] eq $o } keys %opts; - complain($o) unless $key; - - if ($opts{$key}[1]) { - my $arg = shift @ARGV; - complain('-'.$o, 2) unless defined $arg; - $args{$key} .= ( $args{$key} ? ' ' : '' ).$arg; # join with whitespace - } - else { $args{$key}++; } - } - } - else { complain($opt) } + my $opt = shift @ARGV; + if ($opt =~ /^--?$/) { + last; + } + elsif ($opt =~ s/^--([\w-]+)(?:=(.*))?/$1/) { + if (exists $opts{$opt}) { + if ($opts{$opt}[1]) { + my $arg = $2 || shift @ARGV; + complain('--'.$opt, 2) unless defined $arg; + $args{$opt} .= ( $args{$opt} ? ' ' : '' ) . $arg; + } + else { $args{$opt}++ } + } + else { complain('--'.$opt) } + } + elsif ($opt =~ s/^-(?!-)//) { + foreach my $o (split //, $opt) { + my ($key) = grep { $opts{$_}[0] eq $o } keys %opts; + complain($o) unless $key; + + if ($opts{$key}[1]) { + my $arg = shift @ARGV; + complain('-'.$o, 2) unless defined $arg; + $args{$key} .= ( $args{$key} ? ' ' : '' ).$arg; # join with whitespace + } + else { $args{$key}++; } + } + } + else { complain($opt) } } if ($args{help} || $args{usage}) { - eval 'use Pod::Usage'; - die "Could not find perl module Pod::Usage\n" if $@; - pod2usage( { - -verbose => 1, - -exitval => 0, - } ); + eval 'use Pod::Usage'; + die "Could not find perl module Pod::Usage\n" if $@; + pod2usage( { + -verbose => 1, + -exitval => 0, + } ); } if ($args{version}) { - print "mimeopen $VERSION\n\n", << 'EOV'; + print "mimeopen $VERSION\n\n", << 'EOV'; Copyright (c) 2005, 2012 Jaap G Karssenberg. All rights reserved. This program is free software; you can redistribute it and/or modify it under the same terms as Perl. @@ -74,7 +78,7 @@ This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. EOV - exit 0; + exit 0; } complain(undef, 4) unless scalar(@ARGV); @@ -84,7 +88,10 @@ complain(undef, 4) unless scalar(@ARGV); # ############# # # --database -@File::MimeInfo::DIRS = split /:/, $args{database} if $args{database}; +{ + no warnings 'once'; + @File::MimeInfo::DIRS = split /:/, $args{database} if $args{database}; +} ## Actually use our modules ## eval 'use File::MimeInfo::Magic qw/mimetype magic/;'; @@ -92,27 +99,35 @@ die $@ if $@; eval 'use File::MimeInfo::Applications;'; die $@ if $@; + *default = \&File::MimeInfo::default; # --debug if ($args{debug}) { - $File::MimeInfo::DEBUG++; - $File::MimeInfo::Magic::DEBUG++; - print '> Data dirs are: ', join( ', ', - $args{database} - ? ( split /:/, $args{database} ) - : ( - File::BaseDir::xdg_data_home(), - File::BaseDir::xdg_data_dirs() - ) - ), "\n"; + { + no warnings 'once'; + $File::MimeInfo::DEBUG++; + $File::MimeInfo::Magic::DEBUG++; + } + print '> Data dirs are: ', join( ', ', + $args{database} + ? ( split /:/, $args{database} ) + : ( + File::BaseDir::xdg_data_home(), + File::BaseDir::xdg_data_dirs() + ) + ), "\n"; } # --dereference ## deprecated - so always true $args{dereference} = 1; if ($args{dereference}) { - eval 'use File::Spec'; - die "Could not find perl module File::Spec\n" if $@; + eval 'use File::Spec'; + die "Could not find perl module File::Spec\n" if $@; +} + +if (!File::MimeInfo::has_mimeinfo_database()) { + die "No mimeinfo database found\n"; } # ######## # @@ -126,46 +141,45 @@ my $file = $ARGV[0]; my $f = ($args{dereference} && -l $file) ? resolvelink($file) : $file; # --magic-only $mimetype = $args{'magic-only'} - ? (magic($f) || default($f)) - : mimetype($f) ; + ? (magic($f) || default($f)) + : mimetype($f) ; unless (length $mimetype) { - print STDERR "Could not determine mimetype for file: $file\n"; - exit 5; + print STDERR "Could not determine mimetype for file: $file\n"; + exit 5; } my ($default, @other) = mime_applications_all($mimetype); -## Removed this because user should always be able to select "Other..." -#unless($default or @other) { -# print STDERR "No applications found for mimetype: $mimetype\n"; -# exit 6; -#} -## - if ($args{'no-ask'}) { - $default = defined($default) ? $default : $other[0]; + $default = defined($default) ? $default : $other[0]; } elsif ($args{'ask'}) { - $default = choose($mimetype, 0, grep defined($_), $default, @other); + $default = choose($mimetype, 0, grep defined($_), $default, @other); } elsif ($args{'ask-default'}) { - $default = choose($mimetype, 1, grep defined($_), $default, @other); + $default = choose($mimetype, 1, grep defined($_), $default, @other); } elsif (! defined $default) { - ($default) = (@other == 1) ? (@other) : choose($mimetype, 1, @other); + ($default) = (@other == 1) ? (@other) : choose($mimetype, 1, @other); +} + +unless($default) { + # $default can still be undef, if $other[0] is undef and no-ask is set. + print STDERR "No applications found for mimetype: $mimetype\n."; + exit 6; } print 'Opening '.join(', ', map qq{"$_"}, @ARGV) - . ' with '.$default->get_value('Name')." ($mimetype)\n"; + . ' with '.$default->get_value('Name')." ($mimetype)\n"; #print STDERR "exec string: ".$default->parse_Exec(@ARGV)."\n"; if (@ARGV == 1 or $default->wants_list) { - $default->exec(@ARGV); + $default->exec(@ARGV); } else { - my $last = pop @ARGV; - fork or $default->exec($_) for @ARGV; - $default->exec($last); + my $last = pop @ARGV; + fork or $default->exec($_) for @ARGV; + $default->exec($last); } exit 7; # something went wrong in the exec @@ -175,79 +189,79 @@ exit 7; # something went wrong in the exec # ########### # sub choose { - my ($mime, $set_default, @app) = @_; - print $set_default ? - "Please choose a default application for files of type $mime\n\n" : - "Please choose an application\n\n" ; - my @done; - for my $i (0 .. $#app) { - my (undef, undef, $file) = - File::Spec->splitpath( $app[$i]->{file} ); - $file =~ s/\.desktop$//; - if (grep {$_ eq $file} @done) { - $app[$i] = undef; - } - else { - push @done, $file; - print "\t", scalar(@done), ") ", - $app[$i]->get_value('Name'), " ($file)\n"; - } - } - @app = grep defined($_), @app; - print "\t", scalar(@done)+1, ") Other...\n" if $set_default; - print "\nuse application #"; - my $c = <STDIN>; - chomp $c; - - unless ($c =~ /^\d+$/) { - print STDERR "Cancelled\n"; - exit 8; - } - $c--; # base-1 => base-0 - - if ($set_default and $c == scalar(@done)) { - # ask for custom command - print "use command: "; - my $cmd = <STDIN>; - chomp $cmd; - push @app, - eval { mime_applications_set_custom($mime => $cmd) }; - warn $@ if $@; - } - elsif ($c > scalar(@app)) { - print STDERR "Cancelled\n"; - exit 8; - } - elsif ($set_default) { - eval { mime_applications_set_default($mime => $app[$c]) }; - warn $@ if $@; - } - - return $app[$c]; + my ($mime, $set_default, @app) = @_; + print $set_default ? + "Please choose a default application for files of type $mime\n\n" : + "Please choose an application\n\n" ; + my @done; + for my $i (0 .. $#app) { + my (undef, undef, $file) = + File::Spec->splitpath( $app[$i]->{file} ); + $file =~ s/\.desktop$//; + if (grep {$_ eq $file} @done) { + $app[$i] = undef; + } + else { + push @done, $file; + print "\t", scalar(@done), ") ", + $app[$i]->get_value('Name'), " ($file)\n"; + } + } + @app = grep defined($_), @app; + print "\t", scalar(@done)+1, ") Other...\n" if $set_default; + print "\nuse application #"; + my $c = <STDIN>; + chomp $c; + + unless ($c =~ /^\d+$/) { + print STDERR "Cancelled\n"; + exit 8; + } + $c--; # base-1 => base-0 + + if ($set_default and $c == scalar(@done)) { + # ask for custom command + print "use command: "; + my $cmd = <STDIN>; + chomp $cmd; + push @app, + eval { mime_applications_set_custom($mime => $cmd) }; + warn $@ if $@; + } + elsif ($c > scalar(@app)) { + print STDERR "Cancelled\n"; + exit 8; + } + elsif ($set_default) { + eval { mime_applications_set_default($mime => $app[$c]) }; + warn $@ if $@; + } + + return $app[$c]; } sub complain { # Error messages - my $opt = shift; - my $m = shift || 1; - - my $bn = $0; - $bn =~ s|^(.*/)*||; - if ($m == 1) { print STDERR "$bn: unrecognized option '$opt'" } - elsif ($m == 2) { print STDERR "$bn: option '$opt' requires an argument" } - elsif ($m == 3) { print STDERR "$bn: $opt: No such file or directory\n" } - elsif ($m == 4) { print STDERR "usage: $bn [options] files" } - - print "\nTry '$bn --help' for more information.\n" unless $m == 3; - exit $m; + my $opt = shift; + my $m = shift || 1; + + my $bn = $0; + $bn =~ s|^(.*/)*||; + if ($m == 1) { print STDERR "$bn: unrecognized option '$opt'" } + elsif ($m == 2) { print STDERR "$bn: option '$opt' requires an argument" } + elsif ($m == 3) { print STDERR "$bn: $opt: No such file or directory\n" } + elsif ($m == 4) { print STDERR "usage: $bn [options] files" } + + print "\nTry '$bn --help' for more information.\n" unless $m == 3; + exit $m; } sub resolvelink { # --dereference - my $file = shift; - my $link = readlink($file) || return $file; - my (undef, $dir, undef) = File::Spec->splitpath($file); - $link = File::Spec->rel2abs($link, $dir); - $link = resolvelink($link) if -l $link; # recurs - return $link; + my $file = shift; + my $link = readlink($file) || return $file; + my (undef, $dir, undef) = File::Spec->splitpath($file); + $link = File::Spec->rel2abs($link, $dir); + $link = resolvelink($link) if -l $link; # recurs + return $link; } __END__ @@ -1,81 +1,85 @@ #!/usr/bin/perl use strict; -our $VERSION = '0.29'; +use warnings; +our $VERSION = '0.30'; $|++; # ########## # # Parse ARGV # # ########## # +use Encode::Locale; +use Encode; +@ARGV = map { decode(locale => $_, 1) } @ARGV; + my %args = (); my %opts = ( - # name => [char, expect_arg_bit ] - 'help' => ['h'], - 'usage' => ['u'], - 'version' => ['v'], - 'stdin' => [''], - 'brief' => ['b'], - 'namefile' => ['f', 1], - 'noalign' => ['N'], - 'describe' => ['d'], - 'file-compat' => [''], - 'output-format' => ['', 1], - 'language' => ['l', 1], - 'mimetype' => ['i'], - 'dereference' => ['L'], - 'separator' => ['F',1], - 'debug' => ['D'], - 'database' => ['', 1], - 'all' => ['a'], - 'magic-only' => ['M'], + 'help' => ['h'], + 'usage' => ['u'], + 'version' => ['v'], + 'stdin' => [''], + 'brief' => ['b'], + 'namefile' => ['f', 1], + 'noalign' => ['N'], + 'describe' => ['d'], + 'file-compat' => [''], + 'output-format' => ['', 1], + 'language' => ['l', 1], + 'mimetype' => ['i'], + 'dereference' => ['L'], + 'separator' => ['F',1], + 'debug' => ['D'], + 'database' => ['', 1], + 'all' => ['a'], + 'magic-only' => ['M'], ); $args{'file-compat'}++ if $0 =~ m#(^|/)file$#; while ((@ARGV) && ($ARGV[0] =~ /^-/)) { - my $opt = shift @ARGV; - if ($opt =~ /^--?$/) { - $args{stdin}++ if $args{'file-compat'} && $opt eq '-'; - last; - } - elsif ($opt =~ s/^--([\w-]+)(?:=(.*))?/$1/) { - if (exists $opts{$opt}) { - if ($opts{$opt}[1]) { - my $arg = $2 || shift @ARGV; - complain('--'.$opt, 2) unless defined $arg; - $args{$opt} .= ( $args{$opt} ? ' ' : '' ) . $arg; - } - else { $args{$opt}++ } - } - else { complain('--'.$opt) } - } - elsif ($opt =~ s/^-(?!-)//) { - foreach my $o (split //, $opt) { - my ($key) = grep { $opts{$_}[0] eq $o } keys %opts; - complain($o) unless $key; - - if ($opts{$key}[1]) { - my $arg = shift @ARGV; - complain('-'.$o, 2) unless defined $arg; - $args{$key} .= ( $args{$key} ? ' ' : '' ).$arg; # join with whitespace - } - else { $args{$key}++; } - } - } - else { complain($opt) } + my $opt = shift @ARGV; + if ($opt =~ /^--?$/) { + $args{stdin}++ if $args{'file-compat'} && $opt eq '-'; + last; + } + elsif ($opt =~ s/^--([\w-]+)(?:=(.*))?/$1/) { + if (exists $opts{$opt}) { + if ($opts{$opt}[1]) { + my $arg = $2 || shift @ARGV; + complain('--'.$opt, 2) unless defined $arg; + $args{$opt} .= ( $args{$opt} ? ' ' : '' ) . $arg; + } + else { $args{$opt}++ } + } + else { complain('--'.$opt) } + } + elsif ($opt =~ s/^-(?!-)//) { + foreach my $o (split //, $opt) { + my ($key) = grep { $opts{$_}[0] eq $o } keys %opts; + complain($o) unless $key; + + if ($opts{$key}[1]) { + my $arg = shift @ARGV; + complain('-'.$o, 2) unless defined $arg; + $args{$key} .= ( $args{$key} ? ' ' : '' ).$arg; # join with whitespace + } + else { $args{$key}++; } + } + } + else { complain($opt) } } if ($args{help} || $args{usage}) { - eval 'use Pod::Usage'; - die "Could not find perl module Pod::Usage\n" if $@; - pod2usage( { - -verbose => 1, - -exitval => 0, - } ); + eval 'use Pod::Usage'; + die "Could not find perl module Pod::Usage\n" if $@; + pod2usage( { + -verbose => 1, + -exitval => 0, + } ); } if ($args{version}) { - print "mimetype $VERSION\n\n", << 'EOV'; + print "mimetype $VERSION\n\n", << 'EOV'; Copyright (c) 2003, 2012 Jaap G Karssenberg. All rights reserved. This program is free software; you can redistribute it and/or modify it under the same terms as Perl. @@ -84,7 +88,7 @@ This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. EOV - exit 0; + exit 0; } complain(undef, 4) unless scalar(@ARGV) || $args{stdin} || $args{namefile}; @@ -96,7 +100,10 @@ complain(undef, 4) unless scalar(@ARGV) || $args{stdin} || $args{namefile}; our %desc; # desc caching hash # --database -@File::MimeInfo::DIRS = split /:/, $args{database} if $args{database}; +{ + no warnings 'once'; + @File::MimeInfo::DIRS = split /:/, $args{database} if $args{database}; +} ## Actually use our module ## eval 'use File::MimeInfo::Magic qw/mimetype globs inodetype magic describe/;'; @@ -106,16 +113,19 @@ die $@ if $@; # --debug if ($args{debug}) { - $File::MimeInfo::DEBUG++; - $File::MimeInfo::Magic::DEBUG++; - print '> Data dirs are: ', join( ', ', - $args{database} - ? ( split /:/, $args{database} ) - : ( - File::BaseDir::xdg_data_home(), - File::BaseDir::xdg_data_dirs() - ) - ), "\n"; + { + no warnings 'once'; + $File::MimeInfo::DEBUG++; + $File::MimeInfo::Magic::DEBUG++; + } + print '> Data dirs are: ', join( ', ', + $args{database} + ? ( split /:/, $args{database} ) + : ( + File::BaseDir::xdg_data_home(), + File::BaseDir::xdg_data_dirs() + ) + ), "\n"; } # --file-compat @@ -123,40 +133,47 @@ $args{describe}++ if $args{'file-compat'} && !$args{mimetype}; # --namefile if ($args{namefile}) { - open IN, $args{namefile} - || die "Couldn't open file: $args{namefile}\n"; - unshift @ARGV, map {chomp; $_} (<IN>); - close IN; + open IN, $args{namefile} + || die "Couldn't open file: $args{namefile}\n"; + unshift @ARGV, map {chomp; $_} (<IN>); + close IN; } # --language -$File::MimeInfo::LANG = $args{language} if $args{language}; +{ + no warnings 'once'; + $File::MimeInfo::LANG = $args{language} if $args{language}; +} # Formatting stuff my $l = 5; # "STDIN" unless ($args{brief} || $args{noalign}) { - for (@ARGV) { $l = length($_) if $l < length($_) } + for (@ARGV) { $l = length($_) if $l < length($_) } } $args{separator} = ':' unless defined $args{separator}; my $format = $args{'output-format'} - ? parse_format($args{'output-format'}) - : $args{brief} - ? sub { $args{describe} ? desc($_[1]) : $_[1] } - : $args{noalign} - ? sub { ( $_[0], $args{separator}, ' ', $args{describe} ? desc($_[1]) : $_[1] ) } - : sub { ( $_[0], $args{separator}, ' 'x($l + 1 - length($_[0])), - $args{describe} ? desc($_[1]) : $_[1] ) }; + ? parse_format($args{'output-format'}) + : $args{brief} + ? sub { $args{describe} ? desc($_[1]) : $_[1] } + : $args{noalign} + ? sub { ( $_[0], $args{separator}, ' ', $args{describe} ? desc($_[1]) : $_[1] ) } + : sub { ( $_[0], $args{separator}, ' 'x($l + 1 - length($_[0])), + $args{describe} ? desc($_[1]) : $_[1] ) }; # --dereference if ($args{dereference}) { - eval 'use File::Spec'; - die "Could not find perl module File::Spec\n" if $@; + eval 'use File::Spec'; + die "Could not find perl module File::Spec\n" if $@; } # --stdin if ($args{stdin}) { - eval 'use IO::Scalar'; - die "Could not find perl module IO::Scalar\n" if $@; + eval 'use IO::Scalar'; + die "Could not find perl module IO::Scalar\n" if $@; +} + +if (!File::MimeInfo::has_mimeinfo_database()) { + die "No mimeinfo database found\n"; } # ######## # @@ -165,28 +182,33 @@ if ($args{stdin}) { # --stdin if ($args{stdin}) { - my $data; - read(STDIN, $data, $File::MimeInfo::Magic::max_buffer); - my $scalar = new IO::Scalar \$data; - print $format->('STDIN', mimetype($scalar)), "\n"; - exit; + my $data; + { + no warnings 'once'; + read(STDIN, $data, $File::MimeInfo::Magic::max_buffer); + } + my $scalar = new IO::Scalar \$data; + print $format->('STDIN', mimetype($scalar)), "\n"; + exit; } foreach my $file (@ARGV) { - # --dereference - my $f = ($args{dereference} && -l $file) ? resolvelink($file) : $file; - # --magic-only - if ($args{'magic-only'}) { - print $format->($file, magic($f) || default($f)), "\n"; - } - # --all - elsif ($args{all}) { - for (qw#inodetype globs magic default#) { - my $m = eval "$_(\$f)"; - print $format->($file, $m), "\n" if $m; - } - } - else { print $format->($file, mimetype($f)), "\n" } + # --dereference + my $f = ($args{dereference} && -l $file) ? resolvelink($file) : $file; + # --magic-only + if ($args{'magic-only'}) { + print $format->($file, magic($f) || default($f)), "\n"; + } + # --all + elsif ($args{all}) { + for (qw#inodetype globs magic default#) { + my $m = eval "$_(\$f)"; + print $format->($file, $m), "\n" if $m; + } + } + else { + print $format->($file, mimetype($f)), "\n" + } } exit; @@ -196,43 +218,43 @@ exit; # ########### # sub complain { # Error messages - my $opt = shift; - my $m = shift || 1; - - my $bn = $0; - $bn =~ s|^(.*/)*||; - if ($m == 1) { print STDERR "$bn: unrecognized option '$opt'" } - elsif ($m == 2) { print STDERR "$bn: option '$opt' requires an argument" } - elsif ($m == 3) { print STDERR "$bn: $opt: No such file or directory\n" } - elsif ($m == 4) { print STDERR "usage: $bn [options] files" } - - print "\nTry '$bn --help' for more information.\n" unless $m == 3; - exit $m; + my $opt = shift; + my $m = shift || 1; + + my $bn = $0; + $bn =~ s|^(.*/)*||; + if ($m == 1) { print STDERR "$bn: unrecognized option '$opt'" } + elsif ($m == 2) { print STDERR "$bn: option '$opt' requires an argument" } + elsif ($m == 3) { print STDERR "$bn: $opt: No such file or directory\n" } + elsif ($m == 4) { print STDERR "usage: $bn [options] files" } + + print "\nTry '$bn --help' for more information.\n" unless $m == 3; + exit $m; } sub parse_format { # Advanced formatting - my $form = shift; - my $code = "sub { '$form' }"; - # code will get @_ = qw/file type/ - $code =~ s/(?<!\\)%f/'.\$_[0].'/g; - $code =~ s/(?<!\\)%m/'.\$_[1].'/g; - $code =~ s/(?<!\\)%d/'.desc(\$_[1]).'/g; - return eval $code; + my $form = shift; + my $code = "sub { '$form' }"; + # code will get @_ = qw/file type/ + $code =~ s/(?<!\\)%f/'.\$_[0].'/g; + $code =~ s/(?<!\\)%m/'.\$_[1].'/g; + $code =~ s/(?<!\\)%d/'.desc(\$_[1]).'/g; + return eval $code; } sub resolvelink { # --dereference - my $file = shift; - my $link = readlink($file) || return $file; - my (undef, $dir, undef) = File::Spec->splitpath($file); - $link = File::Spec->rel2abs($link, $dir); - $link = resolvelink($link) if -l $link; # recurs - return $link; + my $file = shift; + my $link = readlink($file) || return $file; + my (undef, $dir, undef) = File::Spec->splitpath($file); + $link = File::Spec->rel2abs($link, $dir); + $link = resolvelink($link) if -l $link; # recurs + return $link; } sub desc { # Cache description - my $mt = shift; - return undef unless $mt; - $desc{$mt} ||= describe($mt) || describe($mt, ''); # second form overrules the language settings to default + my $mt = shift; + return undef unless $mt; + $desc{$mt} ||= describe($mt) || describe($mt, ''); # second form overrules the language settings to default } __END__ @@ -348,9 +370,9 @@ Do not align output fields. If you want an alternative output format, you can specify a format string containing the following escapes: - %f for the filename - %d description - %m mime type + %f for the filename + %d description + %m mime type Alignment is not available when using this, you need to post-process the output to do that. @@ -382,9 +404,9 @@ mime-info will be expected in the "mime" sub directory of one of these directories. If these are not set, there will be searched for the following directories: - $HOME/.local/share/mime - /usr/local/share/mime - /usr/share/mime + $HOME/.local/share/mime + /usr/local/share/mime + /usr/share/mime See also the "XDG Base Directory Specification" L<http://freedesktop.org/Standards/basedir-spec> diff --git a/t/000-report-versions-tiny.t b/t/000-report-versions-tiny.t index 12a836a..34c41b9 100644 --- a/t/000-report-versions-tiny.t +++ b/t/000-report-versions-tiny.t @@ -49,13 +49,14 @@ sub pmver { } eval { $v .= pmver('Carp','any version') }; +eval { $v .= pmver('Encode::Locale','any version') }; eval { $v .= pmver('Exporter','any version') }; eval { $v .= pmver('Fcntl','any version') }; -eval { $v .= pmver('Pod::Usage','any version') }; eval { $v .= pmver('File::Spec','0.03') }; eval { $v .= pmver('File::DesktopEntry','0.04') }; -eval { $v .= pmver('Test::More','0.88') }; eval { $v .= pmver('Path::Tiny','any version') }; +eval { $v .= pmver('Pod::Usage','any version') }; +eval { $v .= pmver('Test::More','0.88') }; # All done. diff --git a/t/00_use_ok.t b/t/00_use_ok.t index 776c019..6737b2e 100644 --- a/t/00_use_ok.t +++ b/t/00_use_ok.t @@ -3,7 +3,7 @@ require_ok('File::MimeInfo'); require_ok('File::MimeInfo::Magic'); require_ok('File::MimeInfo::Rox'); SKIP: { - eval "use File::DesktopEntry"; - skip('File::DesktopEntry not installed', 1) if $@; - require_ok('File::MimeInfo::Applications'); + eval "use File::DesktopEntry"; + skip('File::DesktopEntry not installed', 1) if $@; + require_ok('File::MimeInfo::Applications'); } diff --git a/t/01_normal.t b/t/01_normal.t index 9139b18..45f398e 100644 --- a/t/01_normal.t +++ b/t/01_normal.t @@ -1,28 +1,28 @@ - use strict; +use warnings; -use Test::More tests => 31; +use Test::More; $ENV{XDG_DATA_HOME} = './t/'; $ENV{XDG_DATA_DIRS} = './t/'; # forceing non default value -use_ok('File::MimeInfo', qw/mimetype describe globs/); # 1 +use_ok('File::MimeInfo', qw/mimetype describe globs/); # test what was read +File::MimeInfo::rehash(); { - no warnings; # don't bug me because I use these vars only once - File::MimeInfo::rehash(); - ok(scalar(keys %File::MimeInfo::literal) == 1, 'literal data is there'); # 2 - ok(scalar(@File::MimeInfo::globs) == 1, 'globs data is there'); # 3 + no warnings 'once'; + ok(scalar(keys %File::MimeInfo::literal) == 1, 'literal data is there'); + ok(scalar(@File::MimeInfo::globs) == 1, 'globs data is there'); } # test _glob_to_regexp my $i = 0; for my $glob ( - [ '*.pl', [ '(?-xism:^.*\.pl$)', '(?^u:^.*\.pl$)', '(?^:^.*\.pl$)' ] ], # 4 - [ '*.h++', [ '(?-xism:^.*\.h\+\+$)', '(?^u:^.*\.h\+\+$)', '(?^:^.*\.h\+\+$)' ] ], # 5 - [ '*.[tar].*', [ '(?-xism:^.*\.[tar]\..*$)', '(?^u:^.*\.[tar]\..*$)', '(?^:^.*\.[tar]\..*$)' ] ], # 6 - [ '*.?', [ '(?-xism:^.*\..?$)', '(?^u:^.*\..?$)', '(?^:^.*\..?$)' ] ], # 7 + [ '*.pl', [ '(?-xism:^.*\.pl$)', '(?^u:^.*\.pl$)', '(?^:^.*\.pl$)' ] ], + [ '*.h++', [ '(?-xism:^.*\.h\+\+$)', '(?^u:^.*\.h\+\+$)', '(?^:^.*\.h\+\+$)' ] ], + [ '*.[tar].*', [ '(?-xism:^.*\.[tar]\..*$)', '(?^u:^.*\.[tar]\..*$)', '(?^:^.*\.[tar]\..*$)' ] ], + [ '*.?', [ '(?-xism:^.*\..?$)', '(?^u:^.*\..?$)', '(?^:^.*\..?$)' ] ], ) { my $converted = File::MimeInfo::_glob_to_regexp( $glob->[0] ); @@ -39,50 +39,52 @@ for my $glob ( # test parsing file names $i = 0; for ( - ['script.pl', 'application/x-perl'], # 8 - ['script.old.pl', 'application/x-perl'], # 9 - ['script.PL', 'application/x-perl'], # 10 - case insensitive use of glob - ['script.tar.pl', 'application/x-perl'], # 11 - ['script.gz', 'application/x-gzip'], # 12 - ['script.tar.gz', 'application/x-compressed-tar'], # 13 - ['INSTALL', 'text/x-install'], # 14 - ['script.foo.bar.gz', 'application/x-gzip'], # 15 - ['script.foo.tar.gz', 'application/x-compressed-tar'], # 16 - ['makefile', 'text/x-makefile'], # 17 - ['./makefile', 'text/x-makefile'], # 18 + ['script.pl', 'application/x-perl'], + ['script.old.pl', 'application/x-perl'], + ['script.PL', 'application/x-perl'], + ['script.tar.pl', 'application/x-perl'], + ['script.gz', 'application/x-gzip'], + ['script.tar.gz', 'application/x-compressed-tar'], + ['INSTALL', 'text/x-install'], + ['script.foo.bar.gz', 'application/x-gzip'], + ['script.foo.tar.gz', 'application/x-compressed-tar'], + ['makefile', 'text/x-makefile'], + ['./makefile', 'text/x-makefile'], ) { is( mimetype($_->[0]), $_->[1], 'file '.++$i ) } # test OO interface my $ref = File::MimeInfo->new ; -is(ref($ref), q/File::MimeInfo/, 'constructor works'); # 19 -is( $ref->mimetype('script.pl'), 'application/x-perl', 'OO syntax works'); # 20 +is(ref($ref), q/File::MimeInfo/, 'constructor works'); +is( $ref->mimetype('script.pl'), 'application/x-perl', 'OO syntax works'); # test default -is( mimetype('t/default/binary_file'), 'application/octet-stream', 'default works for binary data'); # 21 -is( mimetype('t/default/plain_text'), 'text/plain', 'default works for plain text'); # 22 -is( mimetype('t/default/empty_file'), 'text/plain', 'default works for empty file'); # 23 -ok( ! defined mimetype('t/non_existing_file'), 'default works for non existing file'); # 24 -is( mimetype('t/default/utf8_text'), 'text/plain', 'we speak utf8' ); # 25 -is( mimetype('t/default/encoding_breakage'), 'application/octet-stream', 'encoding bug gone' ); # 26 +is( mimetype('t/default/binary_file'), 'application/octet-stream', 'default works for binary data'); +is( mimetype('t/default/plain_text'), 'text/plain', 'default works for plain text'); +is( mimetype('t/default/empty_file'), 'text/plain', 'default works for empty file'); +ok( ! defined mimetype('t/non_existing_file'), 'default works for non existing file'); +is( mimetype('t/default/utf8_text'), 'text/plain', 'we speak utf8' ); +is( mimetype('t/default/encoding_breakage'), 'application/octet-stream', 'encoding bug gone' ); # test inode thingy -is( mimetype('t'), 'inode/directory', 'directories are recognized'); # 27 +is( mimetype('t'), 'inode/directory', 'directories are recognized'); SKIP: { - unlink 't/symlink' or die "Could not unlink t/symlink" - if -l 't/symlink'; - skip('symlink not supported', 1) - unless eval { symlink("",""); 1 } - and symlink('t/default' => 't/symlink') ; - is( mimetype('t/symlink'), 'inode/symlink', 'symlinks are recognized'); # 28 + unlink 't/symlink' or die "Could not unlink t/symlink" + if -l 't/symlink'; + skip('symlink not supported', 1) + unless eval { symlink("",""); 1 } + and symlink('t/default' => 't/symlink') ; + is( mimetype('t/symlink'), 'inode/symlink', 'symlinks are recognized'); } # test describe -ok( describe('text/plain') eq 'Plain Text', 'describe works' ); # 29 +ok( describe('text/plain') eq 'Plain Text', 'describe works' ); { - no warnings; # don't bug me because I use this var only once - $File::MimeInfo::LANG = 'nl'; + no warnings 'once'; + $File::MimeInfo::LANG = 'nl'; + ok( describe('text/plain') eq 'Platte tekst', 'describe works with other languages' ); } -ok( describe('text/plain') eq 'Platte tekst', 'describe works with other languages' ); # 30 -is( mimetype('t/test.png'), 'image/png', 'glob priority observed'); # 31 +is( mimetype('t/test.png'), 'image/png', 'glob priority observed'); + +done_testing; diff --git a/t/02_magic.t b/t/02_magic.t index 5108d97..dbadf08 100644 --- a/t/02_magic.t +++ b/t/02_magic.t @@ -13,10 +13,10 @@ Test::More->import( tests => (2 * scalar(@files) + 1) ); use_ok('File::MimeInfo::Magic', qw/mimetype magic/); for (@files) { - $type = $_; - $type =~ tr#_#/#; - $type =~ s#\.\w+$##; - ok( mimetype("t/magic/$_") eq $type, "complete (magic) typing of $_"); - undef $type if $type eq "text/plain" || $type eq "application/octet-stream"; - ok( magic("t/magic/$_") eq $type, "magic typing of $_" ); + $type = $_; + $type =~ tr#_#/#; + $type =~ s#\.\w+$##; + ok( mimetype("t/magic/$_") eq $type, "complete (magic) typing of $_"); + undef $type if $type eq "text/plain" || $type eq "application/octet-stream"; + ok( magic("t/magic/$_") eq $type, "magic typing of $_" ); } @@ -1,5 +1,7 @@ +use strict; +use warnings; -use Test::More tests => 2; +use Test::More; $ENV{XDG_DATA_HOME} = './t/'; $ENV{XDG_DATA_DIRS} = './t/'; # forceing non default value @@ -9,9 +11,9 @@ $ENV{CHOICESPATH} = './t'; use_ok(q/File::MimeInfo::Rox/); is_deeply( - [File::MimeInfo::Rox::suggest_script_name('video/mpeg')], - ['./t/MIME-types', 'video_mpeg'], - 'suggest_script_name works' ); + [File::MimeInfo::Rox::suggest_script_name('video/mpeg')], + ['./t/MIME-types', 'video_mpeg'], + 'suggest_script_name works' ); # dunno what more to test :S - +done_testing; diff --git a/t/04_IO_objects.t b/t/04_IO_objects.t index 51fac45..d579121 100644 --- a/t/04_IO_objects.t +++ b/t/04_IO_objects.t @@ -1,5 +1,6 @@ use strict; -require Test::More; +use warnings; +use Test::More; $ENV{XDG_DATA_HOME} = './t/'; $ENV{XDG_DATA_DIRS} = './t/'; # forceing non default value @@ -14,20 +15,20 @@ eval "use File::MimeInfo::Magic"; # force runtime evaluation die $@ if $@; unless (eval 'require IO::Scalar') { - ok(1, 'Skip - no IO::Scalar found') for 0 .. $#files; + ok(1, 'Skip - no IO::Scalar found') for 0 .. $#files; } else { - for (@files) { - my $type = $_; - $type =~ tr#_#/#; + for (@files) { + my $type = $_; + $type =~ tr#_#/#; - open FILE, "t/magic/$_" || die $!; - my $file = join '', (<FILE>); - close FILE; - my $io = new IO::Scalar \$file; + open FILE, "t/magic/$_" || die $!; + my $file = join '', (<FILE>); + close FILE; + my $io = new IO::Scalar \$file; - ok( mimetype($io) eq $type, "typing of $_ as io::scalar" ) - } + ok( mimetype($io) eq $type, "typing of $_ as io::scalar" ) + } } __END__ @@ -35,11 +36,10 @@ __END__ # Not all platforms seem to support <:encoding(latin2) :( unless (eval 'require IO::File') { - ok(1, 'Skip - no IO::File found'); - exit 0; + ok(1, 'Skip - no IO::File found'); + exit 0; } my $io = new IO::File; $io->open('t/text_plain_czech', '<:encoding(latin2)'); ok( mimetype($io) eq 'text/plain', "czech (ISO 8859-2) encoded text" ); - diff --git a/t/05_more.t b/t/05_more.t index d77187b..f57b2ef 100644 --- a/t/05_more.t +++ b/t/05_more.t @@ -1,62 +1,63 @@ use strict; +use warnings; use File::Spec; -use Test::More tests => 16; +use Test::More; $ENV{XDG_DATA_HOME} = './t/'; $ENV{XDG_DATA_DIRS} = './t/'; # forceing non default value -use_ok('File::MimeInfo', qw/extensions mimetype_canon mimetype_isa/); # 1 +use_ok('File::MimeInfo', qw/extensions mimetype_canon mimetype_isa/); ## test reverse extension lookup -ok( extensions('text/plain') eq 'asc', 'extenions works'); # 2 -is_deeply( [extensions('text/plain')], [qw#asc txt#], 'wantarray extensions works' ); # 3 +ok( extensions('text/plain') eq 'asc', 'extenions works'); +is_deeply( [extensions('text/plain')], [qw#asc txt#], 'wantarray extensions works' ); +# call above should have triggered rehash() { - # call above should have triggered rehash() - no warnings; # don't bug me because I use these vars only once - is(scalar(keys %File::MimeInfo::extension), 7, 'extension data is there'); # 4 + no warnings 'once'; + is(scalar(keys %File::MimeInfo::extension), 7, 'extension data is there'); } ## test alias lookup -ok(mimetype_canon('text/plain') eq 'text/plain', 'canon is transparent'); # 5 -ok(mimetype_canon('application/x-pdf') eq 'application/pdf', 'canon works'); # 6 +ok(mimetype_canon('text/plain') eq 'text/plain', 'canon is transparent'); +ok(mimetype_canon('application/x-pdf') eq 'application/pdf', 'canon works'); ## test subclass lookup -ok(mimetype_isa('text/foo', 'text/plain'), 'implicite text/plain subclass'); # 7 -is_deeply([mimetype_isa('text/foo')], [qw(text/plain application/octet-stream)], 'implite application/octet-stream subclass'); # 8 -ok(mimetype_isa('inode/mount-point', 'inode/directory'), 'implicte inode/directory subclass'); # 9 -ok(mimetype_isa('application/x-perl', 'application/x-executable'), 'subclass form file'); # 10 -is_deeply([mimetype_isa('application/x-perl')], [qw(application/x-executable text/plain application/octet-stream)], 'subclass list from file'); # 11 +ok(mimetype_isa('text/foo', 'text/plain'), 'implicite text/plain subclass'); +is_deeply([mimetype_isa('text/foo')], [qw(text/plain application/octet-stream)], 'implite application/octet-stream subclass'); +ok(mimetype_isa('inode/mount-point', 'inode/directory'), 'implicte inode/directory subclass'); +ok(mimetype_isa('application/x-perl', 'application/x-executable'), 'subclass form file'); +is_deeply([mimetype_isa('application/x-perl')], [qw(application/x-executable text/plain application/octet-stream)], 'subclass list from file'); ## Tests for Applications SKIP: { - eval { require File::DesktopEntry }; - skip "File::DesktopEntry not installed", 3 if $@; - - use_ok('File::MimeInfo::Applications'); - - - my %list = ( - 'text/plain' => 'foo.desktop', - 'image/svg+xml' => 'mirage.desktop', - ); - - for my $type (keys %list) { - - my ($default, @other) = mime_applications($type); - ok ( - !defined($default) && - (@other == 1) && - ref($other[0]) eq 'File::DesktopEntry', - 'mime_application() works' - ); - is ( - $other[0]->{file}, - File::Spec->catfile('t', 'applications', $list{$type}), - "desktop file is the right one", - ); - } + eval { require File::DesktopEntry }; + skip "File::DesktopEntry not installed", 3 if $@; + + use_ok('File::MimeInfo::Applications'); + + + my %list = ( + 'text/plain' => 'foo.desktop', + 'image/svg+xml' => 'mirage.desktop', + ); + + for my $type (keys %list) { + + my ($default, @other) = mime_applications($type); + ok ( + !defined($default) && + (@other == 1) && + ref($other[0]) eq 'File::DesktopEntry', + 'mime_application() works' + ); + is ( + $other[0]->{file}, + File::Spec->catfile('t', 'applications', $list{$type}), + "desktop file is the right one", + ); + } } - +done_testing; diff --git a/t/06_pod_ok.t b/t/06_pod_ok.t index 091e399..ff44267 100644 --- a/t/06_pod_ok.t +++ b/t/06_pod_ok.t @@ -1,4 +1,4 @@ use Test::More; eval "use Test::Pod 1.00"; plan skip_all => "Test::Pod 1.00 required for testing POD" if $@; -all_pod_files_ok( all_pod_files(qw/bin lib/) ); +all_pod_files_ok( all_pod_files(qw/mimeopen mimetype lib/) ); diff --git a/t/10filehandle.t b/t/10filehandle.t index 92ad2a9..8862159 100644 --- a/t/10filehandle.t +++ b/t/10filehandle.t @@ -9,6 +9,10 @@ if ($@) { plan skip_all => "module Path::Tiny not installed \n"; } +if (!File::MimeInfo::has_mimeinfo_database()) { + plan skip_all => "No mimeinfo database found \n"; +} + is(mimetype(path('test.png')), 'image/png', 'mimetype of test.png'); is(mimetype(path('../t/test.png')), 'image/png', 'mimetype of file with path'); is(inodetype(path('test.png')), undef, 'inodetype of test.png'); diff --git a/t/11mimeinfo.t b/t/11mimeinfo.t index f8b4c98..01a19ea 100644 --- a/t/11mimeinfo.t +++ b/t/11mimeinfo.t @@ -2,13 +2,20 @@ use strict; use warnings; use Test::More; - +use File::MimeInfo; use File::Spec; +use File::Temp; use FindBin qw($Bin); eval "use IO::Scalar"; my $have_io_scalar = !$@; +if (!File::MimeInfo::has_mimeinfo_database()) { + plan skip_all => "No mimeinfo database found \n"; +} + +my $empty_dir = File::Temp::tempdir(); + my $mimetype_file = File::Spec->catfile($Bin, '..', 'mimetype'); my %tests = ( @@ -19,11 +26,17 @@ my %tests = ( for my $test (sort keys %tests) { my $result = $tests{$test}; is(`$^X $mimetype_file --noalign $test`, "$test: $result\n", $test); + is($?, 0); SKIP: { skip "Skip stdin test because no IO::Scalar", 1 if !$have_io_scalar; is(`$^X $mimetype_file --noalign --stdin < $test`, "STDIN: $result\n", "$test (stdin)"); }; + # with empty mimetype dirs, should exit non-zero + `$^X $mimetype_file --database "$empty_dir" --noalign $test`; + cmp_ok($?, '>', 0); } + + done_testing; |