You cannot select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
	
	
		
			188 lines
		
	
	
		
			5.9 KiB
		
	
	
	
		
			Python
		
	
			
		
		
	
	
			188 lines
		
	
	
		
			5.9 KiB
		
	
	
	
		
			Python
		
	
import os
 | 
						|
import sys
 | 
						|
import stat
 | 
						|
import select
 | 
						|
import time
 | 
						|
import errno
 | 
						|
 | 
						|
try:
 | 
						|
    InterruptedError
 | 
						|
except NameError:
 | 
						|
    # Alias Python2 exception to Python3
 | 
						|
    InterruptedError = select.error
 | 
						|
 | 
						|
if sys.version_info[0] >= 3:
 | 
						|
    string_types = (str,)
 | 
						|
else:
 | 
						|
    string_types = (unicode, str)
 | 
						|
 | 
						|
 | 
						|
def is_executable_file(path):
 | 
						|
    """Checks that path is an executable regular file, or a symlink towards one.
 | 
						|
 | 
						|
    This is roughly ``os.path isfile(path) and os.access(path, os.X_OK)``.
 | 
						|
    """
 | 
						|
    # follow symlinks,
 | 
						|
    fpath = os.path.realpath(path)
 | 
						|
 | 
						|
    if not os.path.isfile(fpath):
 | 
						|
        # non-files (directories, fifo, etc.)
 | 
						|
        return False
 | 
						|
 | 
						|
    mode = os.stat(fpath).st_mode
 | 
						|
 | 
						|
    if (sys.platform.startswith('sunos')
 | 
						|
            and os.getuid() == 0):
 | 
						|
        # When root on Solaris, os.X_OK is True for *all* files, irregardless
 | 
						|
        # of their executability -- instead, any permission bit of any user,
 | 
						|
        # group, or other is fine enough.
 | 
						|
        #
 | 
						|
        # (This may be true for other "Unix98" OS's such as HP-UX and AIX)
 | 
						|
        return bool(mode & (stat.S_IXUSR |
 | 
						|
                            stat.S_IXGRP |
 | 
						|
                            stat.S_IXOTH))
 | 
						|
 | 
						|
    return os.access(fpath, os.X_OK)
 | 
						|
 | 
						|
 | 
						|
def which(filename, env=None):
 | 
						|
    '''This takes a given filename; tries to find it in the environment path;
 | 
						|
    then checks if it is executable. This returns the full path to the filename
 | 
						|
    if found and executable. Otherwise this returns None.'''
 | 
						|
 | 
						|
    # Special case where filename contains an explicit path.
 | 
						|
    if os.path.dirname(filename) != '' and is_executable_file(filename):
 | 
						|
        return filename
 | 
						|
    if env is None:
 | 
						|
        env = os.environ
 | 
						|
    p = env.get('PATH')
 | 
						|
    if not p:
 | 
						|
        p = os.defpath
 | 
						|
    pathlist = p.split(os.pathsep)
 | 
						|
    for path in pathlist:
 | 
						|
        ff = os.path.join(path, filename)
 | 
						|
        if is_executable_file(ff):
 | 
						|
            return ff
 | 
						|
    return None
 | 
						|
 | 
						|
 | 
						|
def split_command_line(command_line):
 | 
						|
 | 
						|
    '''This splits a command line into a list of arguments. It splits arguments
 | 
						|
    on spaces, but handles embedded quotes, doublequotes, and escaped
 | 
						|
    characters. It's impossible to do this with a regular expression, so I
 | 
						|
    wrote a little state machine to parse the command line. '''
 | 
						|
 | 
						|
    arg_list = []
 | 
						|
    arg = ''
 | 
						|
 | 
						|
    # Constants to name the states we can be in.
 | 
						|
    state_basic = 0
 | 
						|
    state_esc = 1
 | 
						|
    state_singlequote = 2
 | 
						|
    state_doublequote = 3
 | 
						|
    # The state when consuming whitespace between commands.
 | 
						|
    state_whitespace = 4
 | 
						|
    state = state_basic
 | 
						|
 | 
						|
    for c in command_line:
 | 
						|
        if state == state_basic or state == state_whitespace:
 | 
						|
            if c == '\\':
 | 
						|
                # Escape the next character
 | 
						|
                state = state_esc
 | 
						|
            elif c == r"'":
 | 
						|
                # Handle single quote
 | 
						|
                state = state_singlequote
 | 
						|
            elif c == r'"':
 | 
						|
                # Handle double quote
 | 
						|
                state = state_doublequote
 | 
						|
            elif c.isspace():
 | 
						|
                # Add arg to arg_list if we aren't in the middle of whitespace.
 | 
						|
                if state == state_whitespace:
 | 
						|
                    # Do nothing.
 | 
						|
                    None
 | 
						|
                else:
 | 
						|
                    arg_list.append(arg)
 | 
						|
                    arg = ''
 | 
						|
                    state = state_whitespace
 | 
						|
            else:
 | 
						|
                arg = arg + c
 | 
						|
                state = state_basic
 | 
						|
        elif state == state_esc:
 | 
						|
            arg = arg + c
 | 
						|
            state = state_basic
 | 
						|
        elif state == state_singlequote:
 | 
						|
            if c == r"'":
 | 
						|
                state = state_basic
 | 
						|
            else:
 | 
						|
                arg = arg + c
 | 
						|
        elif state == state_doublequote:
 | 
						|
            if c == r'"':
 | 
						|
                state = state_basic
 | 
						|
            else:
 | 
						|
                arg = arg + c
 | 
						|
 | 
						|
    if arg != '':
 | 
						|
        arg_list.append(arg)
 | 
						|
    return arg_list
 | 
						|
 | 
						|
 | 
						|
def select_ignore_interrupts(iwtd, owtd, ewtd, timeout=None):
 | 
						|
 | 
						|
    '''This is a wrapper around select.select() that ignores signals. If
 | 
						|
    select.select raises a select.error exception and errno is an EINTR
 | 
						|
    error then it is ignored. Mainly this is used to ignore sigwinch
 | 
						|
    (terminal resize). '''
 | 
						|
 | 
						|
    # if select() is interrupted by a signal (errno==EINTR) then
 | 
						|
    # we loop back and enter the select() again.
 | 
						|
    if timeout is not None:
 | 
						|
        end_time = time.time() + timeout
 | 
						|
    while True:
 | 
						|
        try:
 | 
						|
            return select.select(iwtd, owtd, ewtd, timeout)
 | 
						|
        except InterruptedError:
 | 
						|
            err = sys.exc_info()[1]
 | 
						|
            if err.args[0] == errno.EINTR:
 | 
						|
                # if we loop back we have to subtract the
 | 
						|
                # amount of time we already waited.
 | 
						|
                if timeout is not None:
 | 
						|
                    timeout = end_time - time.time()
 | 
						|
                    if timeout < 0:
 | 
						|
                        return([], [], [])
 | 
						|
            else:
 | 
						|
                # something else caused the select.error, so
 | 
						|
                # this actually is an exception.
 | 
						|
                raise
 | 
						|
 | 
						|
 | 
						|
def poll_ignore_interrupts(fds, timeout=None):
 | 
						|
    '''Simple wrapper around poll to register file descriptors and
 | 
						|
    ignore signals.'''
 | 
						|
 | 
						|
    if timeout is not None:
 | 
						|
        end_time = time.time() + timeout
 | 
						|
 | 
						|
    poller = select.poll()
 | 
						|
    for fd in fds:
 | 
						|
        poller.register(fd, select.POLLIN | select.POLLPRI | select.POLLHUP | select.POLLERR)
 | 
						|
 | 
						|
    while True:
 | 
						|
        try:
 | 
						|
            timeout_ms = None if timeout is None else timeout * 1000
 | 
						|
            results = poller.poll(timeout_ms)
 | 
						|
            return [afd for afd, _ in results]
 | 
						|
        except InterruptedError:
 | 
						|
            err = sys.exc_info()[1]
 | 
						|
            if err.args[0] == errno.EINTR:
 | 
						|
                # if we loop back we have to subtract the
 | 
						|
                # amount of time we already waited.
 | 
						|
                if timeout is not None:
 | 
						|
                    timeout = end_time - time.time()
 | 
						|
                    if timeout < 0:
 | 
						|
                        return []
 | 
						|
            else:
 | 
						|
                # something else caused the select.error, so
 | 
						|
                # this actually is an exception.
 | 
						|
                raise
 |