mirror of
				git://git.code.sf.net/p/zsh/code
				synced 2025-10-31 18:10:56 +01:00 
			
		
		
		
	
		
			
				
	
	
		
			359 lines
		
	
	
	
		
			10 KiB
		
	
	
	
		
			Text
		
	
	
	
	
	
			
		
		
	
	
			359 lines
		
	
	
	
		
			10 KiB
		
	
	
	
		
			Text
		
	
	
	
	
	
| 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 default_type_files default_cap_files
 | |
| local -a type_files cap_files array match mbegin mend
 | |
| local file line type suffix exts elt flags line2
 | |
| integer ind
 | |
| 
 | |
| default_type_files=(~/.mime.types /etc/mime.types)
 | |
| default_cap_files=(~/.mailcap /etc/mailcap)
 | |
| 
 | |
| # Customizable list of files to examine.
 | |
| if zstyle -a :mime: mime-types type_files; then
 | |
|   while (( (ind = ${type_files[(I)+]}) > 0 )); do
 | |
|     type_files[$ind]=($default_type_files)
 | |
|   done
 | |
| else
 | |
|   type_files=($default_type_files)
 | |
| fi
 | |
| 
 | |
| if zstyle -a :mime: mailcap cap_files; then
 | |
|   while (( (ind = ${cap_files[(I)+]}) > 0 )); do
 | |
|     cap_files[$ind]=($default_cap_files)
 | |
|   done
 | |
| else
 | |
|   cap_files=($default_cap_files)
 | |
| fi
 | |
| 
 | |
| {
 | |
|   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
 | |
| } always {
 | |
|   unfunction mime-setup-add-type >&/dev/null
 | |
| }
 | |
| 
 | |
| local -a pats_prio o_prios
 | |
| local o_overwrite sentinel
 | |
| typeset -A type_prio_flags_map type_prio_src_map type_prio_mprio_map
 | |
| integer src_id prio mprio
 | |
| 
 | |
| # A list of keywords indicating the methods used to break ties amongst multiple
 | |
| # entries. The following keywords are accepted:
 | |
| # files: The order of files read: Entries from files read earlier are preferred
 | |
| #   (The default value of the variable is a list with this keyword alone)
 | |
| # priority: The priority flag is matched in the entry. Can be a value from 0 to
 | |
| # 9. The default priority is 5. Higher priorities are preferred.
 | |
| # flags: See the mailcap-prio-flags option
 | |
| # place: Always overrides. Useful for specifying that entries read later are
 | |
| # preferred.
 | |
| #
 | |
| # As the program reads mailcap entries, if it encounters a duplicate
 | |
| # entry, each of the keywords in the list are checked to see if the new
 | |
| # entry can override the existing entry. If none of the keywords are able
 | |
| # to decide whether the new entry should be preferred to the older one, the
 | |
| # new entry is discarded.
 | |
| zstyle -a :mime: mailcap-priorities o_prios || o_prios=(files)
 | |
| 
 | |
| # This style is used as an argument for the flags test in mailcap-priorities.
 | |
| # This is a list of patterns, each of which is tested against the flags for the
 | |
| # mailcap entry. An match with a pattern ahead in the list is preferred as
 | |
| # opposed to a match later in the list. An unmatched item is least preferred.
 | |
| zstyle -a :mime: mailcap-prio-flags pats_prio
 | |
| 
 | |
| # Loop through files to find handlers for types.
 | |
| ((src_id = 0))
 | |
| for file in $cap_files; do
 | |
|   [[ -r $file ]] || continue
 | |
| 
 | |
|   ((src_id = src_id + 1))
 | |
|   # 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:]]}
 | |
| 
 | |
|       ((prio = 0))
 | |
|       for i in $pats_prio; do
 | |
| 	  # print -r "Comparing $i with '$flags'" >&2
 | |
| 	[[ $flags = ${~i} ]] && break
 | |
| 	  # print -r "Comparison failed" >&2
 | |
| 	((prio = prio + 1))
 | |
|       done
 | |
|       ((mprio=5))
 | |
|       [[ $flags = (#b)*priority=([0-9])* ]] && mprio=$match[1]
 | |
|       sentinel=no
 | |
|       if [[ -n $type_handler_map[$type] ]]; then
 | |
| 	for i in $o_prios; do
 | |
| 	  case $i in
 | |
| 	    (files)
 | |
| 	    if [[ $src_id -lt $type_prio_src_map[$type] ]]; then
 | |
| 	      sentinel=yes; break
 | |
| 	    elif [[ $src_id -gt $type_prio_src_map[$type] ]]; then
 | |
| 	      sentinel=no; break
 | |
| 	    fi
 | |
| 	    ;;
 | |
| 	    (priority)
 | |
| 	    if [[ $mprio -gt $type_prio_mprio_map[$type] ]]; then
 | |
| 	      sentinel=yes; break
 | |
| 	    elif [[ $mprio -lt $type_prio_mprio_map[$type] ]]; then
 | |
| 	      sentinel=no; break
 | |
| 	    fi
 | |
| 	    ;;
 | |
| 	    (flags)
 | |
| 	    if [[ $prio -lt $type_prio_flags_map[$type] ]]; then
 | |
| 	      sentinel=yes; break
 | |
| 	    elif [[ $prio -gt $type_prio_flags_map[$type] ]]; then
 | |
| 	      sentinel=no; break
 | |
| 	    fi
 | |
| 	    ;;
 | |
| 	    (place)
 | |
| 	    sentinel=yes
 | |
| 	    break
 | |
| 	    ;;
 | |
| 	  esac
 | |
| 	done
 | |
|       else
 | |
| 	sentinel=yes
 | |
|       fi
 | |
| 
 | |
|       if [[ $sentinel = yes ]]; then
 | |
| 	if [[ -n $o_verbose ]]; then
 | |
| 	  if [[ -n $type_handler_map[$type] ]]; then
 | |
| 	    print -r "Overriding" >&2
 | |
| 	  else
 | |
| 	    print -r "Adding" >&2
 | |
| 	  fi
 | |
| 	  print -r " handler for type $type:
 | |
|   $line" >&2
 | |
| 	fi
 | |
| 	type_handler_map[$type]=$line
 | |
| 	type_flags_map[$type]=$flags
 | |
| 	type_prio_src_map[$type]=$src_id
 | |
| 	type_prio_flags_map[$type]=$prio
 | |
| 	type_prio_mprio_map[$type]=$mprio
 | |
| 	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
 |