Splits up a windows command as the built-in command parser would. Windows has potentially bizarre rules depending on where you look. When spawning a process via the Windows C runtime (which is what python does when you call popen) the rules are as follows: https://docs.microsoft.co
(s)
| 184 | |
| 185 | |
| 186 | def _windows_shell_split(s): |
| 187 | """Splits up a windows command as the built-in command parser would. |
| 188 | |
| 189 | Windows has potentially bizarre rules depending on where you look. When |
| 190 | spawning a process via the Windows C runtime (which is what python does |
| 191 | when you call popen) the rules are as follows: |
| 192 | |
| 193 | https://docs.microsoft.com/en-us/cpp/cpp/parsing-cpp-command-line-arguments |
| 194 | |
| 195 | To summarize: |
| 196 | |
| 197 | * Only space and tab are valid delimiters |
| 198 | * Double quotes are the only valid quotes |
| 199 | * Backslash is interpreted literally unless it is part of a chain that |
| 200 | leads up to a double quote. Then the backslashes escape the backslashes, |
| 201 | and if there is an odd number the final backslash escapes the quote. |
| 202 | |
| 203 | :param s: The command string to split up into parts. |
| 204 | :return: A list of command components. |
| 205 | """ |
| 206 | if not s: |
| 207 | return [] |
| 208 | |
| 209 | components = [] |
| 210 | buff = [] |
| 211 | is_quoted = False |
| 212 | num_backslashes = 0 |
| 213 | for character in s: |
| 214 | if character == '\\': |
| 215 | # We can't simply append backslashes because we don't know if |
| 216 | # they are being used as escape characters or not. Instead we |
| 217 | # keep track of how many we've encountered and handle them when |
| 218 | # we encounter a different character. |
| 219 | num_backslashes += 1 |
| 220 | elif character == '"': |
| 221 | if num_backslashes > 0: |
| 222 | # The backslashes are in a chain leading up to a double |
| 223 | # quote, so they are escaping each other. |
| 224 | buff.append('\\' * int(floor(num_backslashes / 2))) |
| 225 | remainder = num_backslashes % 2 |
| 226 | num_backslashes = 0 |
| 227 | if remainder == 1: |
| 228 | # The number of backslashes is uneven, so they are also |
| 229 | # escaping the double quote, so it needs to be added to |
| 230 | # the current component buffer. |
| 231 | buff.append('"') |
| 232 | continue |
| 233 | |
| 234 | # We've encountered a double quote that is not escaped, |
| 235 | # so we toggle is_quoted. |
| 236 | is_quoted = not is_quoted |
| 237 | |
| 238 | # If there are quotes, then we may want an empty string. To be |
| 239 | # safe, we add an empty string to the buffer so that we make |
| 240 | # sure it sticks around if there's nothing else between quotes. |
| 241 | # If there is other stuff between quotes, the empty string will |
| 242 | # disappear during the joining process. |
| 243 | buff.append('') |
no outgoing calls
no test coverage detected