1
0
mirror of git://git.code.sf.net/p/zsh/code synced 2024-11-19 21:44:11 +01:00
zsh/Functions/MIME/zsh-mime-handler
2003-09-14 19:37:32 +00:00

143 lines
4.7 KiB
Plaintext

# Handler for MIME types using associative arrays
# zsh_mime_handlers and zsh_mime_flags set up by zsh-mime-setup.
#
# The only flags it handles are copiousoutput and needsterminal.
# copiousoutput is assumed to imply needsterminal. Apart from
# those, it tries to be a bit cunning about quoting, which
# can be a nightmare in MIME handling. If it sees something like
# netscape %s
# and it only has one file to handle (the usual case then it will handle it
# internally just by appending a file.)
#
# Anything else is handled by passing to sh -c, which is the only think
# with a high probability of working. If it sees something with
# quotes, e.g.
# /usr/bin/links "%s"
# it will assume someone else has tried to fix the quoting problem and not
# do that. If it sees something with no quotes but other metacharacters,
# e.g.
# cat %s | handler
# then it will do any quoting and pass the result to sh -c.
# So for example if the argument is "My File", the command executed
# is supposedly
# sh -c 'cat My\ File | handler'
#
# This note is mostly here so you can work out what I tried to do when
# it goes horribly wrong.
emulate -L zsh
setopt extendedglob cbases
# We need zformat from zsh/zutil for %s replacement.
zmodload -i zsh/zutil
# Always called with a filename argument first.
# There might be other arguments; don't really know what to do
# with these, but if they came from e.g. `*.ps' then we might
# just as well pass them all down. However, we just take the
# suffix from the first since that's what invoked us via suffix -s.
local suffix context
local -a match mbegin mend
[[ $1 = (#b)*.([^.]##) ]] || return 1
suffix=$match[1]
context=":mime:.${suffix}:"
local handler flags
zstyle -s $context handler handler ||
handler="${zsh_mime_handlers[$suffix]}"
zstyle -s $context flags flags ||
flags="${zsh_mime_flags[$suffix]}"
local -a files
local hasmeta stdin
# See if the handler has shell metacharacters in.
# Don't count whitespace since we can split that when it's unquoted.
if [[ $handler = *[\\\;\*\?\|\"\'\`\$]* ]]; then
hasmeta=1
fi
local -a execargs
if [[ $handler = *%s* ]]; then
# We need to replace %s with the file(s).
local command
if [[ -n $hasmeta || $# -gt 1 ]]; then
# The handler is complicated, either due to special
# characters or multiple files. We are going to pass it
# down to sh, since it's probably written for sh syntax.
#
# See if it's a good idea to quote the filename(s).
# It isn't if there are already quotes in the handler, since
# that means somebody already tried to take account of that.
if [[ $handler = *[\'\"]* ]]; then
# Probably we ought not even to handle multiple
# arguments, but at least the error message ought
# to make it obvious what's going on.
zformat -f command $handler s:"$argv"
else
files=(${(q)argv})
zformat -f command $handler s:"$files"
fi
execargs=(sh -c $command)
else
# Simple command, one filename.
# Split and add the file without extra quoting,
# since later we will just execute the array as is.
for command in ${=handler}; do
zformat -f command $command s:"$1"
execargs+=($command)
done
fi
else
# If there's no %s, the input is supposed to come from stdin.
stdin=1
if [[ -n $hasmeta ]]; then
execargs=(sh -c "$handler")
else
execargs=(${=handler})
fi
fi
# Now execute the command in the appropriate fashion.
if [[ $flags = *copiousoutput* ]]; then
# We need to page the output.
# Careful in case PAGER is a set of commands and arguments.
local -a pager
zstyle -a $context pager pager || pager=(${=PAGER:-more})
if [[ -n $stdin ]]; then
cat $argv | $execargs | $pager
else
$execargs | eval ${PAGER:-more}
fi
elif [[ $flags = *needsterminal* || -z $DISPLAY ]]; then
# Needs a terminal, so run synchronously.
# Obviously, if $DISPLAY is empty but the handler needs a
# GUI we are in trouble anyway. However, it's possible for
# the handler to be smart about this, like pick-web-browser,
# and even if it just produces an error message it's better to
# have it run synchronously.
if [[ -n $stdin ]]; then
cat $argv | $execargs
else
$execargs
fi
else
# Doesn't need a terminal and we have a $DISPLAY, so run
# it in the background. sh probably isn't smart enough to
# exec the last command in the list, but it's not a big deal.
#
# The following Rococo construction is to try to make
# the job output for the backgrounded command descriptive.
# Otherwise it's equivalent to removing the eval and all the quotes,
# including the (q) flags.
if [[ -n $stdin ]]; then
eval cat ${(q)argv} "|" ${(q)execargs} "&"
else
eval ${(q)execargs} "&"
fi
fi