Blame lib/contrib/which/which.py

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