1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142
|
# 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
|