1*061da546Spatrickimport os 2*061da546Spatrickimport sys 3*061da546Spatrickimport stat 4*061da546Spatrickimport select 5*061da546Spatrickimport time 6*061da546Spatrickimport errno 7*061da546Spatrick 8*061da546Spatricktry: 9*061da546Spatrick InterruptedError 10*061da546Spatrickexcept NameError: 11*061da546Spatrick # Alias Python2 exception to Python3 12*061da546Spatrick InterruptedError = select.error 13*061da546Spatrick 14*061da546Spatrickif sys.version_info[0] >= 3: 15*061da546Spatrick string_types = (str,) 16*061da546Spatrickelse: 17*061da546Spatrick string_types = (unicode, str) 18*061da546Spatrick 19*061da546Spatrick 20*061da546Spatrickdef is_executable_file(path): 21*061da546Spatrick """Checks that path is an executable regular file, or a symlink towards one. 22*061da546Spatrick 23*061da546Spatrick This is roughly ``os.path isfile(path) and os.access(path, os.X_OK)``. 24*061da546Spatrick """ 25*061da546Spatrick # follow symlinks, 26*061da546Spatrick fpath = os.path.realpath(path) 27*061da546Spatrick 28*061da546Spatrick if not os.path.isfile(fpath): 29*061da546Spatrick # non-files (directories, fifo, etc.) 30*061da546Spatrick return False 31*061da546Spatrick 32*061da546Spatrick mode = os.stat(fpath).st_mode 33*061da546Spatrick 34*061da546Spatrick if (sys.platform.startswith('sunos') 35*061da546Spatrick and os.getuid() == 0): 36*061da546Spatrick # When root on Solaris, os.X_OK is True for *all* files, irregardless 37*061da546Spatrick # of their executability -- instead, any permission bit of any user, 38*061da546Spatrick # group, or other is fine enough. 39*061da546Spatrick # 40*061da546Spatrick # (This may be true for other "Unix98" OS's such as HP-UX and AIX) 41*061da546Spatrick return bool(mode & (stat.S_IXUSR | 42*061da546Spatrick stat.S_IXGRP | 43*061da546Spatrick stat.S_IXOTH)) 44*061da546Spatrick 45*061da546Spatrick return os.access(fpath, os.X_OK) 46*061da546Spatrick 47*061da546Spatrick 48*061da546Spatrickdef which(filename, env=None): 49*061da546Spatrick '''This takes a given filename; tries to find it in the environment path; 50*061da546Spatrick then checks if it is executable. This returns the full path to the filename 51*061da546Spatrick if found and executable. Otherwise this returns None.''' 52*061da546Spatrick 53*061da546Spatrick # Special case where filename contains an explicit path. 54*061da546Spatrick if os.path.dirname(filename) != '' and is_executable_file(filename): 55*061da546Spatrick return filename 56*061da546Spatrick if env is None: 57*061da546Spatrick env = os.environ 58*061da546Spatrick p = env.get('PATH') 59*061da546Spatrick if not p: 60*061da546Spatrick p = os.defpath 61*061da546Spatrick pathlist = p.split(os.pathsep) 62*061da546Spatrick for path in pathlist: 63*061da546Spatrick ff = os.path.join(path, filename) 64*061da546Spatrick if is_executable_file(ff): 65*061da546Spatrick return ff 66*061da546Spatrick return None 67*061da546Spatrick 68*061da546Spatrick 69*061da546Spatrickdef split_command_line(command_line): 70*061da546Spatrick 71*061da546Spatrick '''This splits a command line into a list of arguments. It splits arguments 72*061da546Spatrick on spaces, but handles embedded quotes, doublequotes, and escaped 73*061da546Spatrick characters. It's impossible to do this with a regular expression, so I 74*061da546Spatrick wrote a little state machine to parse the command line. ''' 75*061da546Spatrick 76*061da546Spatrick arg_list = [] 77*061da546Spatrick arg = '' 78*061da546Spatrick 79*061da546Spatrick # Constants to name the states we can be in. 80*061da546Spatrick state_basic = 0 81*061da546Spatrick state_esc = 1 82*061da546Spatrick state_singlequote = 2 83*061da546Spatrick state_doublequote = 3 84*061da546Spatrick # The state when consuming whitespace between commands. 85*061da546Spatrick state_whitespace = 4 86*061da546Spatrick state = state_basic 87*061da546Spatrick 88*061da546Spatrick for c in command_line: 89*061da546Spatrick if state == state_basic or state == state_whitespace: 90*061da546Spatrick if c == '\\': 91*061da546Spatrick # Escape the next character 92*061da546Spatrick state = state_esc 93*061da546Spatrick elif c == r"'": 94*061da546Spatrick # Handle single quote 95*061da546Spatrick state = state_singlequote 96*061da546Spatrick elif c == r'"': 97*061da546Spatrick # Handle double quote 98*061da546Spatrick state = state_doublequote 99*061da546Spatrick elif c.isspace(): 100*061da546Spatrick # Add arg to arg_list if we aren't in the middle of whitespace. 101*061da546Spatrick if state == state_whitespace: 102*061da546Spatrick # Do nothing. 103*061da546Spatrick None 104*061da546Spatrick else: 105*061da546Spatrick arg_list.append(arg) 106*061da546Spatrick arg = '' 107*061da546Spatrick state = state_whitespace 108*061da546Spatrick else: 109*061da546Spatrick arg = arg + c 110*061da546Spatrick state = state_basic 111*061da546Spatrick elif state == state_esc: 112*061da546Spatrick arg = arg + c 113*061da546Spatrick state = state_basic 114*061da546Spatrick elif state == state_singlequote: 115*061da546Spatrick if c == r"'": 116*061da546Spatrick state = state_basic 117*061da546Spatrick else: 118*061da546Spatrick arg = arg + c 119*061da546Spatrick elif state == state_doublequote: 120*061da546Spatrick if c == r'"': 121*061da546Spatrick state = state_basic 122*061da546Spatrick else: 123*061da546Spatrick arg = arg + c 124*061da546Spatrick 125*061da546Spatrick if arg != '': 126*061da546Spatrick arg_list.append(arg) 127*061da546Spatrick return arg_list 128*061da546Spatrick 129*061da546Spatrick 130*061da546Spatrickdef select_ignore_interrupts(iwtd, owtd, ewtd, timeout=None): 131*061da546Spatrick 132*061da546Spatrick '''This is a wrapper around select.select() that ignores signals. If 133*061da546Spatrick select.select raises a select.error exception and errno is an EINTR 134*061da546Spatrick error then it is ignored. Mainly this is used to ignore sigwinch 135*061da546Spatrick (terminal resize). ''' 136*061da546Spatrick 137*061da546Spatrick # if select() is interrupted by a signal (errno==EINTR) then 138*061da546Spatrick # we loop back and enter the select() again. 139*061da546Spatrick if timeout is not None: 140*061da546Spatrick end_time = time.time() + timeout 141*061da546Spatrick while True: 142*061da546Spatrick try: 143*061da546Spatrick return select.select(iwtd, owtd, ewtd, timeout) 144*061da546Spatrick except InterruptedError: 145*061da546Spatrick err = sys.exc_info()[1] 146*061da546Spatrick if err.args[0] == errno.EINTR: 147*061da546Spatrick # if we loop back we have to subtract the 148*061da546Spatrick # amount of time we already waited. 149*061da546Spatrick if timeout is not None: 150*061da546Spatrick timeout = end_time - time.time() 151*061da546Spatrick if timeout < 0: 152*061da546Spatrick return([], [], []) 153*061da546Spatrick else: 154*061da546Spatrick # something else caused the select.error, so 155*061da546Spatrick # this actually is an exception. 156*061da546Spatrick raise 157*061da546Spatrick 158*061da546Spatrick 159*061da546Spatrickdef poll_ignore_interrupts(fds, timeout=None): 160*061da546Spatrick '''Simple wrapper around poll to register file descriptors and 161*061da546Spatrick ignore signals.''' 162*061da546Spatrick 163*061da546Spatrick if timeout is not None: 164*061da546Spatrick end_time = time.time() + timeout 165*061da546Spatrick 166*061da546Spatrick poller = select.poll() 167*061da546Spatrick for fd in fds: 168*061da546Spatrick poller.register(fd, select.POLLIN | select.POLLPRI | select.POLLHUP | select.POLLERR) 169*061da546Spatrick 170*061da546Spatrick while True: 171*061da546Spatrick try: 172*061da546Spatrick timeout_ms = None if timeout is None else timeout * 1000 173*061da546Spatrick results = poller.poll(timeout_ms) 174*061da546Spatrick return [afd for afd, _ in results] 175*061da546Spatrick except InterruptedError: 176*061da546Spatrick err = sys.exc_info()[1] 177*061da546Spatrick if err.args[0] == errno.EINTR: 178*061da546Spatrick # if we loop back we have to subtract the 179*061da546Spatrick # amount of time we already waited. 180*061da546Spatrick if timeout is not None: 181*061da546Spatrick timeout = end_time - time.time() 182*061da546Spatrick if timeout < 0: 183*061da546Spatrick return [] 184*061da546Spatrick else: 185*061da546Spatrick # something else caused the select.error, so 186*061da546Spatrick # this actually is an exception. 187*061da546Spatrick raise 188