summary refs log tree commit diff
path: root/Functions/MIME/zsh-mime-handler
diff options
context:
space:
mode:
Diffstat (limited to 'Functions/MIME/zsh-mime-handler')
-rw-r--r--Functions/MIME/zsh-mime-handler142
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