1
0
Fork 0
mirror of git://git.code.sf.net/p/zsh/code synced 2025-09-02 22:11:54 +02:00

19053 modified, c.f. 19056

This commit is contained in:
Peter Stephenson 2003-09-14 19:37:32 +00:00
parent cd89b92a0d
commit 3efb2ec394
6 changed files with 677 additions and 2 deletions

View file

@ -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

View file

@ -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)

View file

@ -0,0 +1,3 @@
DISTFILES_SRC='
zsh-mime-setup zsh-mime-handler pick-web-browser
'

View file

@ -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

View file

@ -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

View file

@ -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