#!/bin/zsh
#
# gwhence - generic version of builtin "whence" that works on a user-
# specifiable path.
#
# usage:
#   gwhence [-a] [-m] [-s path] name . . .
#
# where
#   -a   causes all matching names to be printed, not just the first
#        one that occurs in the path.
#   -m   allows name(s) to be shell patterns (implies -a)
#   -s   lets you supply a colon-separated path for gwhence to
#        search for name(s) (default is $PATH)
#

local opt elem names found all pattern ng

local usage="Usage: $0:t [-am] [-s path] name . . ."

# no arguments - return with zero exit status
if [[ $# -eq 0 ]]
then
  return 0
fi

# search path defaults to $PATH
local search="$PATH"

# fetch options
while getopts :ams: opt
do
  case "$opt" in
    'a') all=true ;;
    'm') pattern=true ;;
    's') search="$OPTARG" ;;
    ':') [[ "$OPTARG" = 's' ]] && echo "$usage" ;;
    '?') echo "$usage" ; return 1 ;;
  esac
done

# put options behind us
shift $[ $OPTIND - 1 ]

# remember state of nullglob
[[ -o nullglob ]] && ng=true
setopt nullglob

# if none found, must report it by nonzero exit status at end
found=

# for each name supplied . . .
for names
do
  # scan path, in pieces (colon is separator)
  for elem in ${(s/:/)=search}
  do
    # pattern matching (-m)
    if [[ "$pattern" = "true" ]]
    then
      # for compatibility with whence, must put next line here, not inside
      # following if construct
      found=true
      if [[ -n "$(eval 'echo -n $elem/'$names'')" ]]
      then
        # Must output them on separate lines
        eval 'echo $elem/'$names' | tr '\'' '\'' '\''\n'\'
      fi
    else
      if [[ -a $elem/$names ]]
      then
        echo $elem/$names
        # we found one - remember it
        found=true
      fi
      # note that "found" only applies if -m was not specified
      if [[ "$found" = "true" && "$all" != "true" ]]
      then
        break
      fi 
    fi
  done
done

# reset nullglob status
[[ "$ng" = "true" ]] || unsetopt nullglob

# a name not found? 
if [[ "$found" != "true" ]]
then
  return 1
fi
# exit safely
return 0
