#!/usr/bin/perl -w # Copyright © 2001 by Julian Fong . All rights reserved. # # Permission to use, copy, modify, distribute, and sell this software # and its documentation for any purpose is hereby granted without fee, # provided that the above copyright notice appear in all copies and # that both that copyright notice and this permission notice appear in # supporting documentation. No representations are made about the # suitability of this software for any purpose. It is provided "as # is" without express or implied warranty. # # # start: utility for automatically starting a "viewer" for a file, # similar to the way the "start" command from cmd.exe works on # Windows. Determines file type from mime.types by extension, or uses # file -i if necessary. File type is then used in conjunction with # mailcap to determine what is actually run. # # Bugs: # - only supports part of RFC 1524 (mailcap specification) # - return value from exec cannot be verified because of system # shell usage (mailcap commands need to be passed to shell) use strict; use Getopt::Std; sub gettype { my ($extension, $filename) = @_; my ($next, $type); $type = undef; if (open (MIMETYPES, $filename)) { while () { # Fix continuation lines (more idiomatic way of doing this?) while (/\\$/) { chomp; # eat newline chop; # eat slash last unless defined($next = ); $_ .= $next; } next if /^\s*$/; next if /^\s*\#/; # Netscape style extension map file (is this documented # somewhere?) last if (($type) = /^(?:type|enc)=(\S+).*\bexts=\"([^\"]*\b)?$extension\b/); # NCSA httpd 1.0 and Mosaic style extension map - see # http://archive.ncsa.uiuc.edu/SDG/Software/Mosaic/Docs/extension-map.html # Hopefully, no legitimate type has '=' in it! last if (($type) = /^([^\s=]+)\s+.*\b$extension\b/); } close MIMETYPES; } return $type; } sub gethandler { my ($type, $filename) = @_; my ($handler, $line, $next, $flags); $handler = undef; if (open (MAILCAP, $filename)) { while () { while (/\\$/) { chomp; # eat newline chop; # eat slash last unless defined($next = ); $_ .= $next; } next if /^\s*$/; next if /^\s*\#/; # Should be a useful line at this point chomp; # Deal with protected semicolons before splitting s/\\;/0xFF/g; @_ = split /\s*\;\s*/; # Turn the mailcap type into a regexp for matching # to deal with globbing if (@_ > 1) { $_[0] =~ s/\*/\.\*/g; # Check it against our type if ($type =~ /^$_[0]$/) { $handler = $_[1]; # Restore semicolon $handler =~ s/0xFF/\;/; } } # Keep processing, the last match wins } } return $handler; } my ( %options, $pos, $extension, $filetype, $type, $handler, ); getopts('n', \%options); unless (@ARGV == 1) { die "Usage: start \n"; } unless (-f $ARGV[0]) { die "$ARGV[0] is not a file\n"; } $type = undef; # Get the file extension $pos = rindex ($ARGV[0], "."); if ($pos > 0) { $extension = substr($ARGV[0], $pos + 1); # Look up its type in various mime.types $type = gettype($extension, "$ENV{HOME}/.mime.types"); $type = gettype($extension, "/etc/mime.types") unless (defined($type)); } # If this doesn't work, run "file -i" on it if (!defined($type)) { open (FILE, "file -i $ARGV[0]|") || die "Can't run \"file\" to determine file type\n"; $type = ; if (defined($type)) { chomp($type); $type =~ s/^[^:]*:\s*//; # Throw away anything after the comma, because file may return # "text/plain, ASCII" , which we don't care about at this # point. (We should probably handle them as a mime subtype # though.) if (($pos = index($type, ",")) > 0) { $type = substr($type, 0, $pos); } } else { die "Unexpected result from \"file\" command\n"; } close FILE; } # Look up handler in mailcap $handler = gethandler($type, "$ENV{HOME}/.mailcap"); $handler = gethandler($type, "/etc/mailcap") unless (defined($handler)); die "No handler for type: $type\n" unless (defined($handler)); # %s is replaced with the filename. # If no %s exists, must pass file to stdin if ($handler =~ /%s/) { $handler =~ s/%s/$ARGV[0]/g; } else { $handler = "cat $ARGV[0] | $handler"; } # %t is replaced with the type $handler =~ s/%t/$type/g; # Parameters are ignored $handler =~ s/%{[^}]*}//g; # All other percent translations not handled $handler =~ s/([^\\])%./$1/g; # Unescape escaped text $handler =~ s/\\(.)/$1/g; if ($options{"n"}) { print "$handler\n"; } else { exec $handler || die ("Couldn't run '$handler': $!\n"); # pointless, because we're using shell }