diff options
Diffstat (limited to 'Functions/MIME/zsh-mime-handler')
-rw-r--r-- | Functions/MIME/zsh-mime-handler | 142 |
1 files changed, 142 insertions, 0 deletions
diff --git a/Functions/MIME/zsh-mime-handler b/Functions/MIME/zsh-mime-handler new file mode 100644 index 000000000..b64fd54cd --- /dev/null +++ b/Functions/MIME/zsh-mime-handler @@ -0,0 +1,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 |