mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-31 22:25:30 +00:00
336 lines
12 KiB
Python
336 lines
12 KiB
Python
|
#!/usr/bin/env python
|
||
|
# Copyright (c) 2002-2005 ActiveState Corp.
|
||
|
# See LICENSE.txt for license details.
|
||
|
# Author:
|
||
|
# Trent Mick (TrentM@ActiveState.com)
|
||
|
# Home:
|
||
|
# http://trentm.com/projects/which/
|
||
|
|
||
|
r"""Find the full path to commands.
|
||
|
|
||
|
which(command, path=None, verbose=0, exts=None)
|
||
|
Return the full path to the first match of the given command on the
|
||
|
path.
|
||
|
|
||
|
whichall(command, path=None, verbose=0, exts=None)
|
||
|
Return a list of full paths to all matches of the given command on
|
||
|
the path.
|
||
|
|
||
|
whichgen(command, path=None, verbose=0, exts=None)
|
||
|
Return a generator which will yield full paths to all matches of the
|
||
|
given command on the path.
|
||
|
|
||
|
By default the PATH environment variable is searched (as well as, on
|
||
|
Windows, the AppPaths key in the registry), but a specific 'path' list
|
||
|
to search may be specified as well. On Windows, the PATHEXT environment
|
||
|
variable is applied as appropriate.
|
||
|
|
||
|
If "verbose" is true then a tuple of the form
|
||
|
(<fullpath>, <matched-where-description>)
|
||
|
is returned for each match. The latter element is a textual description
|
||
|
of where the match was found. For example:
|
||
|
from PATH element 0
|
||
|
from HKLM\SOFTWARE\...\perl.exe
|
||
|
"""
|
||
|
|
||
|
_cmdlnUsage = """
|
||
|
Show the full path of commands.
|
||
|
|
||
|
Usage:
|
||
|
which [<options>...] [<command-name>...]
|
||
|
|
||
|
Options:
|
||
|
-h, --help Print this help and exit.
|
||
|
-V, --version Print the version info and exit.
|
||
|
|
||
|
-a, --all Print *all* matching paths.
|
||
|
-v, --verbose Print out how matches were located and
|
||
|
show near misses on stderr.
|
||
|
-q, --quiet Just print out matches. I.e., do not print out
|
||
|
near misses.
|
||
|
|
||
|
-p <altpath>, --path=<altpath>
|
||
|
An alternative path (list of directories) may
|
||
|
be specified for searching.
|
||
|
-e <exts>, --exts=<exts>
|
||
|
Specify a list of extensions to consider instead
|
||
|
of the usual list (';'-separate list, Windows
|
||
|
only).
|
||
|
|
||
|
Show the full path to the program that would be run for each given
|
||
|
command name, if any. Which, like GNU's which, returns the number of
|
||
|
failed arguments, or -1 when no <command-name> was given.
|
||
|
|
||
|
Near misses include duplicates, non-regular files and (on Un*x)
|
||
|
files without executable access.
|
||
|
"""
|
||
|
|
||
|
__revision__ = "$Id: which.py 430 2005-08-20 03:11:58Z trentm $"
|
||
|
__version_info__ = (1, 1, 0)
|
||
|
__version__ = '.'.join(map(str, __version_info__))
|
||
|
|
||
|
import os
|
||
|
import sys
|
||
|
import getopt
|
||
|
import stat
|
||
|
|
||
|
|
||
|
#---- exceptions
|
||
|
|
||
|
class WhichError(Exception):
|
||
|
pass
|
||
|
|
||
|
|
||
|
|
||
|
#---- internal support stuff
|
||
|
|
||
|
def _getRegisteredExecutable(exeName):
|
||
|
"""Windows allow application paths to be registered in the registry."""
|
||
|
registered = None
|
||
|
if sys.platform.startswith('win'):
|
||
|
if os.path.splitext(exeName)[1].lower() != '.exe':
|
||
|
exeName += '.exe'
|
||
|
import _winreg
|
||
|
try:
|
||
|
key = "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\App Paths\\" +\
|
||
|
exeName
|
||
|
value = _winreg.QueryValue(_winreg.HKEY_LOCAL_MACHINE, key)
|
||
|
registered = (value, "from HKLM\\"+key)
|
||
|
except _winreg.error:
|
||
|
pass
|
||
|
if registered and not os.path.exists(registered[0]):
|
||
|
registered = None
|
||
|
return registered
|
||
|
|
||
|
def _samefile(fname1, fname2):
|
||
|
if sys.platform.startswith('win'):
|
||
|
return ( os.path.normpath(os.path.normcase(fname1)) ==\
|
||
|
os.path.normpath(os.path.normcase(fname2)) )
|
||
|
else:
|
||
|
return os.path.samefile(fname1, fname2)
|
||
|
|
||
|
def _cull(potential, matches, verbose=0):
|
||
|
"""Cull inappropriate matches. Possible reasons:
|
||
|
- a duplicate of a previous match
|
||
|
- not a disk file
|
||
|
- not executable (non-Windows)
|
||
|
If 'potential' is approved it is returned and added to 'matches'.
|
||
|
Otherwise, None is returned.
|
||
|
"""
|
||
|
for match in matches: # don't yield duplicates
|
||
|
if _samefile(potential[0], match[0]):
|
||
|
if verbose:
|
||
|
sys.stderr.write("duplicate: %s (%s)\n" % potential)
|
||
|
return None
|
||
|
else:
|
||
|
if not stat.S_ISREG(os.stat(potential[0]).st_mode):
|
||
|
if verbose:
|
||
|
sys.stderr.write("not a regular file: %s (%s)\n" % potential)
|
||
|
elif not os.access(potential[0], os.X_OK):
|
||
|
if verbose:
|
||
|
sys.stderr.write("no executable access: %s (%s)\n"\
|
||
|
% potential)
|
||
|
else:
|
||
|
matches.append(potential)
|
||
|
return potential
|
||
|
|
||
|
|
||
|
#---- module API
|
||
|
|
||
|
def whichgen(command, path=None, verbose=0, exts=None):
|
||
|
"""Return a generator of full paths to the given command.
|
||
|
|
||
|
"command" is a the name of the executable to search for.
|
||
|
"path" is an optional alternate path list to search. The default it
|
||
|
to use the PATH environment variable.
|
||
|
"verbose", if true, will cause a 2-tuple to be returned for each
|
||
|
match. The second element is a textual description of where the
|
||
|
match was found.
|
||
|
"exts" optionally allows one to specify a list of extensions to use
|
||
|
instead of the standard list for this system. This can
|
||
|
effectively be used as an optimization to, for example, avoid
|
||
|
stat's of "foo.vbs" when searching for "foo" and you know it is
|
||
|
not a VisualBasic script but ".vbs" is on PATHEXT. This option
|
||
|
is only supported on Windows.
|
||
|
|
||
|
This method returns a generator which yields either full paths to
|
||
|
the given command or, if verbose, tuples of the form (<path to
|
||
|
command>, <where path found>).
|
||
|
"""
|
||
|
matches = []
|
||
|
if path is None:
|
||
|
usingGivenPath = 0
|
||
|
path = os.environ.get("PATH", "").split(os.pathsep)
|
||
|
if sys.platform.startswith("win"):
|
||
|
path.insert(0, os.curdir) # implied by Windows shell
|
||
|
else:
|
||
|
usingGivenPath = 1
|
||
|
|
||
|
# Windows has the concept of a list of extensions (PATHEXT env var).
|
||
|
if sys.platform.startswith("win"):
|
||
|
if exts is None:
|
||
|
exts = os.environ.get("PATHEXT", "").split(os.pathsep)
|
||
|
# If '.exe' is not in exts then obviously this is Win9x and
|
||
|
# or a bogus PATHEXT, then use a reasonable default.
|
||
|
for ext in exts:
|
||
|
if ext.lower() == ".exe":
|
||
|
break
|
||
|
else:
|
||
|
exts = ['.COM', '.EXE', '.BAT']
|
||
|
elif not isinstance(exts, list):
|
||
|
raise TypeError("'exts' argument must be a list or None")
|
||
|
else:
|
||
|
if exts is not None:
|
||
|
raise WhichError("'exts' argument is not supported on "\
|
||
|
"platform '%s'" % sys.platform)
|
||
|
exts = []
|
||
|
|
||
|
# File name cannot have path separators because PATH lookup does not
|
||
|
# work that way.
|
||
|
if os.sep in command or os.altsep and os.altsep in command:
|
||
|
pass
|
||
|
else:
|
||
|
for i in range(len(path)):
|
||
|
dirName = path[i]
|
||
|
# On windows the dirName *could* be quoted, drop the quotes
|
||
|
if sys.platform.startswith("win") and len(dirName) >= 2\
|
||
|
and dirName[0] == '"' and dirName[-1] == '"':
|
||
|
dirName = dirName[1:-1]
|
||
|
for ext in ['']+exts:
|
||
|
absName = os.path.abspath(
|
||
|
os.path.normpath(os.path.join(dirName, command+ext)))
|
||
|
if os.path.isfile(absName):
|
||
|
if usingGivenPath:
|
||
|
fromWhere = "from given path element %d" % i
|
||
|
elif not sys.platform.startswith("win"):
|
||
|
fromWhere = "from PATH element %d" % i
|
||
|
elif i == 0:
|
||
|
fromWhere = "from current directory"
|
||
|
else:
|
||
|
fromWhere = "from PATH element %d" % (i-1)
|
||
|
match = _cull((absName, fromWhere), matches, verbose)
|
||
|
if match:
|
||
|
if verbose:
|
||
|
yield match
|
||
|
else:
|
||
|
yield match[0]
|
||
|
match = _getRegisteredExecutable(command)
|
||
|
if match is not None:
|
||
|
match = _cull(match, matches, verbose)
|
||
|
if match:
|
||
|
if verbose:
|
||
|
yield match
|
||
|
else:
|
||
|
yield match[0]
|
||
|
|
||
|
|
||
|
def which(command, path=None, verbose=0, exts=None):
|
||
|
"""Return the full path to the first match of the given command on
|
||
|
the path.
|
||
|
|
||
|
"command" is a the name of the executable to search for.
|
||
|
"path" is an optional alternate path list to search. The default it
|
||
|
to use the PATH environment variable.
|
||
|
"verbose", if true, will cause a 2-tuple to be returned. The second
|
||
|
element is a textual description of where the match was found.
|
||
|
"exts" optionally allows one to specify a list of extensions to use
|
||
|
instead of the standard list for this system. This can
|
||
|
effectively be used as an optimization to, for example, avoid
|
||
|
stat's of "foo.vbs" when searching for "foo" and you know it is
|
||
|
not a VisualBasic script but ".vbs" is on PATHEXT. This option
|
||
|
is only supported on Windows.
|
||
|
|
||
|
If no match is found for the command, a WhichError is raised.
|
||
|
"""
|
||
|
try:
|
||
|
match = whichgen(command, path, verbose, exts).next()
|
||
|
except StopIteration:
|
||
|
raise WhichError("Could not find '%s' on the path." % command)
|
||
|
return match
|
||
|
|
||
|
|
||
|
def whichall(command, path=None, verbose=0, exts=None):
|
||
|
"""Return a list of full paths to all matches of the given command
|
||
|
on the path.
|
||
|
|
||
|
"command" is a the name of the executable to search for.
|
||
|
"path" is an optional alternate path list to search. The default it
|
||
|
to use the PATH environment variable.
|
||
|
"verbose", if true, will cause a 2-tuple to be returned for each
|
||
|
match. The second element is a textual description of where the
|
||
|
match was found.
|
||
|
"exts" optionally allows one to specify a list of extensions to use
|
||
|
instead of the standard list for this system. This can
|
||
|
effectively be used as an optimization to, for example, avoid
|
||
|
stat's of "foo.vbs" when searching for "foo" and you know it is
|
||
|
not a VisualBasic script but ".vbs" is on PATHEXT. This option
|
||
|
is only supported on Windows.
|
||
|
"""
|
||
|
return list( whichgen(command, path, verbose, exts) )
|
||
|
|
||
|
|
||
|
|
||
|
#---- mainline
|
||
|
|
||
|
def main(argv):
|
||
|
all = 0
|
||
|
verbose = 0
|
||
|
altpath = None
|
||
|
exts = None
|
||
|
try:
|
||
|
optlist, args = getopt.getopt(argv[1:], 'haVvqp:e:',
|
||
|
['help', 'all', 'version', 'verbose', 'quiet', 'path=', 'exts='])
|
||
|
except getopt.GetoptError, msg:
|
||
|
sys.stderr.write("which: error: %s. Your invocation was: %s\n"\
|
||
|
% (msg, argv))
|
||
|
sys.stderr.write("Try 'which --help'.\n")
|
||
|
return 1
|
||
|
for opt, optarg in optlist:
|
||
|
if opt in ('-h', '--help'):
|
||
|
print _cmdlnUsage
|
||
|
return 0
|
||
|
elif opt in ('-V', '--version'):
|
||
|
print "which %s" % __version__
|
||
|
return 0
|
||
|
elif opt in ('-a', '--all'):
|
||
|
all = 1
|
||
|
elif opt in ('-v', '--verbose'):
|
||
|
verbose = 1
|
||
|
elif opt in ('-q', '--quiet'):
|
||
|
verbose = 0
|
||
|
elif opt in ('-p', '--path'):
|
||
|
if optarg:
|
||
|
altpath = optarg.split(os.pathsep)
|
||
|
else:
|
||
|
altpath = []
|
||
|
elif opt in ('-e', '--exts'):
|
||
|
if optarg:
|
||
|
exts = optarg.split(os.pathsep)
|
||
|
else:
|
||
|
exts = []
|
||
|
|
||
|
if len(args) == 0:
|
||
|
return -1
|
||
|
|
||
|
failures = 0
|
||
|
for arg in args:
|
||
|
#print "debug: search for %r" % arg
|
||
|
nmatches = 0
|
||
|
for match in whichgen(arg, path=altpath, verbose=verbose, exts=exts):
|
||
|
if verbose:
|
||
|
print "%s (%s)" % match
|
||
|
else:
|
||
|
print match
|
||
|
nmatches += 1
|
||
|
if not all:
|
||
|
break
|
||
|
if not nmatches:
|
||
|
failures += 1
|
||
|
return failures
|
||
|
|
||
|
|
||
|
if __name__ == "__main__":
|
||
|
sys.exit( main(sys.argv) )
|
||
|
|
||
|
|