about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--ChangeLog6
-rw-r--r--Doc/Zsh/contrib.yo157
-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
6 files changed, 677 insertions, 2 deletions
diff --git a/ChangeLog b/ChangeLog
index 849e62793..856f8c634 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,9 @@
+2003-09-14  Peter Stephenson  <pws@pwstephenson.fsnet.co.uk>
+
+	* 19053 modified c.f. 19056: Functions/MIME, Doc/Zsh/contrib.yo:
+	Functions using suffix aliases for handling suffixes mailcap
+	style.
+
 2003-09-14  Clint Adams  <clint@zsh.org>
 
 	* 19076: Src/Modules/terminfo.c: don't call setupterm
diff --git a/Doc/Zsh/contrib.yo b/Doc/Zsh/contrib.yo
index 819ae6a98..cf1bada4b 100644
--- a/Doc/Zsh/contrib.yo
+++ b/Doc/Zsh/contrib.yo
@@ -13,6 +13,7 @@ startmenu()
 menu(Utilities)
 menu(Prompt Themes)
 menu(ZLE Functions)
+menu(MIME functions)
 menu(Other Functions)
 endmenu()
 
@@ -344,7 +345,7 @@ normally call a theme's setup function directly.
 )
 enditem()
 
-texinode(ZLE Functions)(Other Functions)(Prompt Themes)(User Contributions)
+texinode(ZLE Functions)(MIME Functions)(Prompt Themes)(User Contributions)
 sect(ZLE Functions)
 
 subsect(Widgets)
@@ -955,7 +956,159 @@ whether the tt(widget) style is used.
 )
 enditem()
 
-texinode(Other Functions)()(ZLE Functions)(User Contributions)
+texinode(MIME Functions)(Other Functions)(ZLE Functions)(User Contributions)
+sect(MIME Functions)
+
+Three functions are available to provide handling of files recognised by
+extension, for example to dispatch a file tt(text.ps) when executed as a
+command to an appropriate viewer.
+
+startitem()
+xitem(tt(zsh-mime-setup [-flv]))
+item(tt(zsh-mime-handler))(
+These two functions use the files tt(~/.mime.types) and tt(/etc/mime.types),
+which associate types and extensions, as well as tt(~/.mailcap) and
+tt(/etc/mailcap) files, which associate types and the programs that
+handle them.  These are provided on many systems with the Multimedia
+Internet Mail Extensions.
+
+To enable the system, the function tt(zsh-mime-setup) should be
+autoloaded and run.  This allows files with extensions to be treated
+as executable; such files be completed by the function completion system.
+The function tt(zsh-mime-handler) should not need to be called by the
+user.
+
+The system works by setting up suffix aliases with `tt(alias -s)'.
+Suffix aliases already installed by the user will not be overwritten.
+
+Repeated calls to tt(zsh-mime-setup) do not override the existing
+mapping between suffixes and executable files unless the option tt(-f)
+is given.  Note, however, that this does not override existing suffix
+aliases assigned to handlers other than tt(zsh-mime-handler).
+Calling tt(zsh-mime-setup) with the option tt(-l) lists the existing
+mapping without altering it.  Calling tt(zsh-mime-setup) with the option
+tt(-v) causes verbose output to be shown during the setup operation.
+
+The system respects the tt(mailcap) flags tt(needsterminal) and
+tt(copiousoutput), see manref(mailcap)(4).
+
+The functions use the following styles, which are defined with the
+tt(zstyle) builtin command (\
+ifzman(see zmanref(zshmodules))\
+ifnzman(noderef(The zsh/zutil Module))).  They should be defined
+before tt(zsh-mime-setup) is run.  The contexts used all
+start with tt(:mime:), with additional components in some cases.
+It is recommended that a trailing tt(*) (suitably quoted) be appended
+to style patterns in case the system is extended in future.  Some
+examples are given below.
+startitem()
+item(mime-types)(
+A list of files in the format of tt(~/.mime.types) and
+tt(/etc/mime.types) to be read during setup, replacing the default list
+which consists of those two files.  The context is tt(:mime:).
+)
+item(mailcap)(
+A list of files in the format of tt(~/.mailcap) and
+tt(/etc/mailcap) to be read during setup, replacing the default list
+which consists of those two files.  The context is tt(:mime:).
+)
+item(handler)(
+Specifies a handler for a suffix; the suffix is given by the context as
+tt(:mime:.)var(suffix)tt(:), and the format of the handler is exactly
+that in tt(mailcap).  Note in particular the `tt(.)' and trailing colon
+to distinguish this use of the context.  This overrides any handler
+specified by the tt(mailcap) files.  If the handler requires a terminal,
+the tt(flags) style should be set to include the word tt(needsterminal),
+or if the output is to be displayed through a pager (but not if the
+handler is itself a pager), it should include tt(copiousoutput).
+)
+item(flags)(
+Defines flags to go with a handler; the context is as for the
+tt(handler) style, and the format is as for the flags in tt(mailcap).
+)
+item(pager)(
+If set, will be used instead of tt($PAGER) or tt(more) to handle
+suffixes where the tt(copiousoutput) flag is set.  The context is
+as for tt(handler), i.e. tt(:mime:.)var(suffix)tt(:) for handling
+a file with the given var(suffix).
+)
+enditem()
+
+Examples:
+
+example(zstyle ':mime:*' mailcap ~/.mailcap /usr/local/etc/mailcap
+zstyle ':mime:.txt' handler less %s
+zstyle ':mime:.txt' flags needsterminal)
+
+When tt(zsh-mime-setup) is subsequently run, it will look for
+tt(mailcap) entries in the two files given.  Files of suffix tt(.txt)
+will be handled by running `tt(less) var(file.txt)'.  The flag
+tt(needsterminal) is set to show that this program must run attached to a
+terminal.
+
+As there are several steps to dispatching a command, the following
+should be checked if attempting to execute a file by extension
+tt(.)var(ext) does not have the expected effect.
+starteit()
+eit()(
+The command `tt(alias -s) var(ext)' should show
+`tt(ps=zsh-mime-handler)'.  If it shows something else, another suffix
+alias was already installed and was not overwritten.  If it shows
+nothing, no handler was installed:  this is most likely because no
+handler was found in the tt(.mime.types) and tt(mailcap) combination for
+tt(.ext) files.  In that case, appropriate handling should be added to
+tt(~/.mime.types) and tt(mailcap).
+)
+eit()(
+If the extension is handled by tt(zsh-mime-handler) but the file is
+not opened correctly, either the handler defined for the type is
+incorrect, or the flags associated with it are in appropriate.  Running
+tt(zsh-mime-setup -l) will show the handler and, if there are any, the
+flags.  A tt(%s) in the handler is replaced by the file (suitably quoted
+if necessary).  Check that the handler program listed lists and can
+be run in the way shown.  Also check that the flags tt(needsterminal) or
+tt(copiousoutput) are set if the handler needs to be run under a
+terminal; the second flag is used if the output should be sent to a pager.
+An example of a suitable tt(mailcap) entry for such a program is:
+
+example(text/html; /usr/bin/lynx '%s'; needsterminal)
+)
+endeit()
+)
+item(tt(pick-web-browser))(
+This function is separate from the two MIME functions described above
+and can be assigned directly to a suffix:
+
+example(autoload -U pick-web-browser
+alias -s html=pick-web-browser)
+
+It is provided as an intelligent front end to dispatch a web browser.
+It will check if an X Windows display is available, and if so
+if there is already a browser running which can accept a remote
+connection.  In that case, the file will be displayed in that browser;
+you should check explicitly if it has appeared in the running browser's
+window.  Otherwise, it will start a new browser according to a builtin
+set of preferences.
+
+Alternatively, tt(pick-web-browser) can be run as a zsh script.
+
+Two styles are available to customize the choice of browsers:
+tt(x-browsers) when running under the X Windows System, and
+tt(tty-browsers) otherwise.  These are arrays in decreasing order
+of preference consiting of the command name under which to start the
+browser.  They are looked up in the context tt(:mime:) (which may
+be extended in future, so appending `tt(*)' is recommended).  For
+example,
+
+example(zstyle ':mime:*' x-browsers opera konqueror netscape)
+
+specifies that tt(pick-web-browser) should first look for a runing
+instance of Opera, Konqueror or Netscape, in that order, and if it
+fails to find any should attempt to start Opera.
+)
+enditem()
+
+texinode(Other Functions)()(MIME Functions)(User Contributions)
 sect(Other Functions)
 
 There are a large number of helpful functions in the tt(Functions/Misc)
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