#!/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
}