summaryrefslogtreecommitdiff
path: root/Functions
diff options
context:
space:
mode:
Diffstat (limited to 'Functions')
-rw-r--r--Functions/MIME/.distfiles3
-rw-r--r--Functions/MIME/pick-web-browser112
-rw-r--r--Functions/MIME/zsh-mime-handler142
-rw-r--r--Functions/MIME/zsh-mime-setup259
4 files changed, 516 insertions, 0 deletions
diff --git a/Functions/MIME/.distfiles b/Functions/MIME/.distfiles
new file mode 100644
index 000000000..c6b05b777
--- /dev/null
+++ b/Functions/MIME/.distfiles
@@ -0,0 +1,3 @@
+DISTFILES_SRC='
+zsh-mime-setup zsh-mime-handler pick-web-browser
+'
diff --git a/Functions/MIME/pick-web-browser b/Functions/MIME/pick-web-browser
new file mode 100644
index 000000000..6f4650c12
--- /dev/null
+++ b/Functions/MIME/pick-web-browser
@@ -0,0 +1,112 @@
+# Function to find a web browser to run on a URL or file.
+# Can also be run as a script. It is suitable for use as
+# a suffix alias:
+# alias -s html=pick-web-browser
+#
+# The single argument is the URL or file name which may be of any type.
+# The only processing which occurs is that if the argument is a file,
+# it is converted into a URL. As the function takes account of
+# any necessary conversions to the file name (for example, if it
+# contains spaces), it is generally preferable to pass in raw file
+# names rather than convert them to URLs elsewhere.
+#
+# The function takes account of the fact that many X Windows browsers
+# which are already running on the current display can take a command
+# to pass the URL to that process for handling. A typical sign
+# that this has happened is that apparently nothing happens --- you
+# need to check the browser window.
+#
+# If no $DISPLAY is set, the function tries to start a terminal-based
+# browser instead.
+
+emulate -L zsh
+setopt extendedglob cbases nonomatch
+
+local -a xbrowsers ttybrowsers
+
+# X Windows browsers which might be running and can accept
+# a remote URL. You can change the order of preference.
+# If none is already running, starts the first in the array.
+zstyle -a :mime: x-browsers xbrowsers ||
+ xbrowsers=(mozilla netscape opera konqueror)
+# Preferred command line browser. Used if there is on $DISPLAY set.
+zstyle -a :mime: tty-browsers ttybrowsers ||
+ ttybrowsers=(links lynx)
+# Characters in addition to alphanumerics which can appear literally
+# in a URL. `-' should be the first if it appears, so append others
+# to the end.
+litc="-_./"
+
+local -a windows remoteargs match mbegin mend
+local url browser
+
+url=$1
+if [[ -f $url ]]; then
+ if [[ $url = *[^-_[:alnum:]]* ]]; then
+ # Convert special characters into hex escapes.
+ local sofar
+ while [[ $url = (#b)([${litc}[:alnum:]]#)([^${litc}[:alnum:]])(*) ]]
+ do
+ sofar+="$match[1]%${$(( [#16] ##$match[2] ))##0x}"
+ url=$match[3]
+ done
+ url="$sofar$url"
+ fi
+
+ # Turn this into a local URL
+ if [[ $url = /* ]]; then
+ url=file://$url
+ else
+ url=file://$PWD/$url
+ fi
+fi
+
+
+if [[ -n $DISPLAY ]]; then
+ # X Windows running
+
+ # Get the name of all windows running; use the internal name, not
+ # the friendly name, which is less useful.
+ #
+ # The nasty but portable version.
+ # The nice but non-portable version uses Perl, even though perl
+ # is more portable.
+# windows=(${(f)"$(xwininfo -root -all |
+# sed -ne 's/.*".*": ("\(.*\)" ".*").*/\1/p' |sort | uniq)"})
+
+ windows=(${(f)"$(xwininfo -root -all |
+ perl -ne '/.*"(.*)": \("(.*)" "(.*)"\).*/ and $w{$2} = 1;
+ END { print join("\n", keys %w), "\n" }')"})
+
+ # Is any browser we've heard of running?
+ for browser in $xbrowsers; do
+ if [[ $windows[(I)(#i)$browser] -ne 0 ]]; then
+ if [[ $browser = konqueror ]]; then
+ # I'm sure there's documentation for this somewhere...
+ dcop $(dcop|grep konqueror) default openBrowserWindow $url
+ else
+ # Mozilla bells and whistles are described at:
+ # http://www.mozilla.org/unix/remote.html
+ $browser -remote "openURL($url)"
+ fi
+ return
+ fi
+ done
+
+ # Start our preferred X Windows browser in the background.
+ for browser in $xbrowsers; do
+ if eval "[[ =$browser != \\=$browser ]]"; then
+ # The following is to make the job text more readable.
+ eval ${(q)browser} ${(q)url} "&"
+ break
+ fi
+ done
+else
+ # Start up dumb terminal browser.
+ for browser in $ttybrowsers; do
+ if eval "[[ =$browser != \\=$browser ]]"; then
+ $browser $url
+ break
+ fi
+ done
+fi
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
diff --git a/Functions/MIME/zsh-mime-setup b/Functions/MIME/zsh-mime-setup
new file mode 100644
index 000000000..5f9168341
--- /dev/null
+++ b/Functions/MIME/zsh-mime-setup
@@ -0,0 +1,259 @@
+emulate -L zsh
+setopt extendedglob cbases
+
+local opt o_verbose o_list
+
+autoload -U zsh-mime-handler
+
+while getopts "flv" opt; do
+ case $opt in
+ # List: show existing suffixes and their handlers then exit.
+ (l)
+ o_list=1
+ ;;
+
+ # Verbose; print diagnostics to stdout.
+ (v)
+ o_verbose=1
+ ;;
+
+ # Force; discard any existing settings before reading.
+ (f)
+ unset -m zsh_mime_\*
+ ;;
+
+ (*)
+ [[ $opt = \? ]] || print -r "Option $opt not handled, complain" >&2
+ return 1
+ ;;
+ esac
+done
+(( OPTIND > 1 )) && shift $(( OPTIND - 1 ))
+
+
+if [[ -n $o_list ]]; then
+ # List and return.
+ for suffix in ${(ko)zsh_mime_handlers}; do
+ print ${(r.10.)suffix}${zsh_mime_handlers[$suffix]}
+ if [[ -n ${zsh_mime_flags[$suffix]} ]]; then
+ print " flags: ${zsh_mime_flags[$suffix]}"
+ fi
+ done
+ return 0
+fi
+
+
+# Handler for each suffix.
+(( ${+zsh_mime_handlers} )) || typeset -gA zsh_mime_handlers
+# Corresponding flags, if any, for handler
+(( ${+zsh_mime_flags} )) || typeset -gA zsh_mime_flags
+
+# Internal maps read from MIME configuration files.
+# Note we don't remember the types, just the mappings from suffixes
+# to handlers and their flags.
+typeset -A suffix_type_map type_handler_map type_flags_map
+
+local -a type_files cap_files array match mbegin mend
+local file line type suffix exts elt flags line2
+
+# Customizable list of files to examine.
+zstyle -a :mime: mime-types type_files ||
+ type_files=(~/.mime.types /etc/mime.types)
+zstyle -a :mime: mailcap cap_files ||
+ cap_files=(~/.mailcap /etc/mailcap)
+
+TRAPEXIT() { unfunction mime-setup-add-type >&/dev/null; return 0; }
+
+mime-setup-add-type() {
+ local type suffix
+ local -a array
+
+ type=$1
+ shift
+
+ while (( $# )); do
+ # `.ps' instead of `ps' has been noted
+ suffix=${1##.}
+ shift
+
+ if [[ -z $suffix_type_map[$suffix] ]]; then
+ [[ -n $o_verbose ]] &&
+ print -r "Adding type $type for $suffix" >&2
+ suffix_type_map[$suffix]=$type
+ else
+ # Skip duplicates.
+ array=(${=suffix_type_map[$suffix]})
+ if [[ ${array[(I)$type]} -eq 0 ]]; then
+ [[ -n $o_verbose ]] &&
+ print -r "Appending type $type for already defined $suffix" >&2
+ suffix_type_map[$suffix]+=" $type"
+ fi
+ fi
+ done
+}
+
+# Loop through files to find suffixes for MIME types.
+# Earlier entries take precedence, so the files need to be listed
+# with the user's own first. This also means pre-existing
+# values in suffix_type_map are respected.
+for file in $type_files; do
+ [[ -r $file ]] || continue
+
+ # For once we rely on the fact that read handles continuation
+ # lines ending in backslashes, i.e. there's no -r.
+ while read line; do
+ # Skip blank or comment lines.
+ [[ $line = [[:space:]]#(\#*|) ]] && continue
+
+ # There are two types of line you find in MIME type files.
+ # The original simple sort contains the type name then suffixes
+ # separated by whitespace. However, Netscape insists
+ # on adding lines with backslash continuation with
+ # key="value" pairs. So we'd better handle both.
+ if [[ $line = *=* ]]; then
+ # Gory.
+ # This relies on the fact that a typical entry:
+ # type=video/x-mpeg2 desc="MPEG2 Video" exts="mpv2,mp2v"
+ # looks like a parameter assignment. However, we really
+ # don't want to be screwed up by future extensions,
+ # so we split the elements to an array and pick out the
+ # ones we're interested in.
+ type= exts=
+
+ # Syntactically split line to preserve quoted words.
+ array=(${(z)line})
+ for elt in $array; do
+ if [[ $elt = (type|exts)=* ]]; then
+ eval $elt
+ fi
+ done
+
+ # Get extensions by splitting on comma
+ array=(${(s.,.)exts})
+
+ [[ -n $type ]] && mime-setup-add-type $type $array
+ else
+ # Simple.
+ mime-setup-add-type ${=line}
+ fi
+ done <$file
+done
+
+
+# Loop through files to find handlers for types.
+for file in $cap_files; do
+ [[ -r $file ]] || continue
+
+ # Oh, great. We need to preserve backslashes inside the line,
+ # but need to manage continuation lines.
+ while read -r line; do
+ # Skip blank or comment lines.
+ [[ $line = [[:space:]]#(\#*|) ]] && continue
+
+ while [[ $line = (#b)(*)\\ ]]; do
+ line=$match[1]
+ read -r line2 || break
+ line+=$line2
+ done
+
+ # Guess what, this file has a completely different format.
+ # See mailcap(4).
+ # The biggest unpleasantness here is that the fields are
+ # delimited by semicolons, but the command field, which
+ # is the one we want to extract, may itself contain backslashed
+ # semicolons.
+ if [[ $line = (#b)[[:space:]]#([^[:space:]\;]##)[[:space:]]#\;(*) ]]
+ then
+ # this is the only form we can handle, but there's no point
+ # issuing a warning for other forms.
+ type=$match[1]
+ line=$match[2]
+ # See if it has flags after the command.
+ if [[ $line = (#b)(([^\;\\]|\\\;|\\[^\;])#)\;(*) ]]; then
+ line=$match[1]
+ flags=$match[3]
+ else
+ flags=
+ fi
+ # Remove quotes from semicolons
+ line=${line//\\\;/\;}
+ # and remove any surrounding white space --- this might
+ # make the handler empty.
+ line=${${line##[[:space:]]#}%%[[:space:]]}
+ if [[ -z $type_handler_map[$type] ]]; then
+ if [[ -n $o_verbose ]]; then
+ print -r "Adding handler for type $type:
+ $line" >&2
+ fi
+ type_handler_map[$type]=$line
+ type_flags_map[$type]=$flags
+ if [[ -n $flags && -n $o_verbose ]]; then
+ print -r " with flags $flags" >&2
+ fi
+ elif [[ -n $o_verbose ]]; then
+ print -r "Skipping handler for already defined type $type:
+ $line" >&2
+ if [[ -n $flags ]]; then
+ print -r " with flags $flags" >&2
+ fi
+ fi
+ fi
+ done <$file
+done
+
+
+# Check for styles which override whatever is in the file.
+# We need to make sure there is a handler set up; for some
+# uses we may need to defer checking styles until zsh-mime-handler.
+# How much we need to do here is a moot point.
+zstyle -L | while read line; do
+ array=(${(Q)${(z)line}})
+ if [[ $array[3] = (handler|flags) && \
+ $array[2] = (#b):mime:.([^:]##):(*) ]]; then
+ suffix=$match[1]
+ # Make sure there is a suffix alias set up for this.
+ alias -s $suffix >&/dev/null || alias -s $suffix=zsh-mime-handler
+ fi
+done
+
+# Now associate the suffixes directly with handlers.
+# We just look for the first one with a handler.
+# If there is no handler, we don't bother registering an alias
+# for the suffix.
+
+for suffix line in ${(kv)suffix_type_map}; do
+ # Skip if we already have a handler.
+ [[ -n $zsh_mime_handlers[$suffix] ]] && continue
+
+ # Split the space-separated list of types.
+ array=(${=line})
+
+ # Find the first type with a handler.
+ line2=
+ for type in $array; do
+ line2=${type_handler_map[$type]}
+ [[ -n $line2 ]] && break
+ done
+
+ # See if there is a generic type/* handler.
+ # TODO: do we need to consider other forms of wildcard?
+ if [[ -z $line2 ]]; then
+ for type in $array; do
+ type="${type%%/*}/*"
+ line2=${type_handler_map[$type]}
+ [[ -n $line2 ]] && break
+ done
+ fi
+
+ if [[ -n $line2 ]]; then
+ # Found a type with a handler.
+ # Install the zsh handler as an alias, but never override
+ # existing suffix handling.
+ alias -s $suffix >&/dev/null || alias -s $suffix=zsh-mime-handler
+
+ zsh_mime_handlers[$suffix]=$line2
+ zsh_mime_flags[$suffix]=$type_flags_map[$type]
+ fi
+done
+
+true