1d9cf8291SDaniel Hwang# -*- coding: utf-8 -*- 2d9cf8291SDaniel Hwang# Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 3d9cf8291SDaniel Hwang# See https://llvm.org/LICENSE.txt for license information. 4d9cf8291SDaniel Hwang# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 5d9cf8291SDaniel Hwang""" This module implements basic shell escaping/unescaping methods. """ 6d9cf8291SDaniel Hwang 7d9cf8291SDaniel Hwangimport re 8d9cf8291SDaniel Hwangimport shlex 9d9cf8291SDaniel Hwang 10*dd3c26a0STobias Hieta__all__ = ["encode", "decode"] 11d9cf8291SDaniel Hwang 12d9cf8291SDaniel Hwang 13d9cf8291SDaniel Hwangdef encode(command): 14d9cf8291SDaniel Hwang """Takes a command as list and returns a string.""" 15d9cf8291SDaniel Hwang 16d9cf8291SDaniel Hwang def needs_quote(word): 17d9cf8291SDaniel Hwang """Returns true if arguments needs to be protected by quotes. 18d9cf8291SDaniel Hwang 19d9cf8291SDaniel Hwang Previous implementation was shlex.split method, but that's not good 20d9cf8291SDaniel Hwang for this job. Currently is running through the string with a basic 21d9cf8291SDaniel Hwang state checking.""" 22d9cf8291SDaniel Hwang 23*dd3c26a0STobias Hieta reserved = { 24*dd3c26a0STobias Hieta " ", 25*dd3c26a0STobias Hieta "$", 26*dd3c26a0STobias Hieta "%", 27*dd3c26a0STobias Hieta "&", 28*dd3c26a0STobias Hieta "(", 29*dd3c26a0STobias Hieta ")", 30*dd3c26a0STobias Hieta "[", 31*dd3c26a0STobias Hieta "]", 32*dd3c26a0STobias Hieta "{", 33*dd3c26a0STobias Hieta "}", 34*dd3c26a0STobias Hieta "*", 35*dd3c26a0STobias Hieta "|", 36*dd3c26a0STobias Hieta "<", 37*dd3c26a0STobias Hieta ">", 38*dd3c26a0STobias Hieta "@", 39*dd3c26a0STobias Hieta "?", 40*dd3c26a0STobias Hieta "!", 41*dd3c26a0STobias Hieta } 42d9cf8291SDaniel Hwang state = 0 43d9cf8291SDaniel Hwang for current in word: 44d9cf8291SDaniel Hwang if state == 0 and current in reserved: 45d9cf8291SDaniel Hwang return True 46*dd3c26a0STobias Hieta elif state == 0 and current == "\\": 47d9cf8291SDaniel Hwang state = 1 48*dd3c26a0STobias Hieta elif state == 1 and current in reserved | {"\\"}: 49d9cf8291SDaniel Hwang state = 0 50d9cf8291SDaniel Hwang elif state == 0 and current == '"': 51d9cf8291SDaniel Hwang state = 2 52d9cf8291SDaniel Hwang elif state == 2 and current == '"': 53d9cf8291SDaniel Hwang state = 0 54d9cf8291SDaniel Hwang elif state == 0 and current == "'": 55d9cf8291SDaniel Hwang state = 3 56d9cf8291SDaniel Hwang elif state == 3 and current == "'": 57d9cf8291SDaniel Hwang state = 0 58d9cf8291SDaniel Hwang return state != 0 59d9cf8291SDaniel Hwang 60d9cf8291SDaniel Hwang def escape(word): 61d9cf8291SDaniel Hwang """Do protect argument if that's needed.""" 62d9cf8291SDaniel Hwang 63*dd3c26a0STobias Hieta table = {"\\": "\\\\", '"': '\\"'} 64*dd3c26a0STobias Hieta escaped = "".join([table.get(c, c) for c in word]) 65d9cf8291SDaniel Hwang 66d9cf8291SDaniel Hwang return '"' + escaped + '"' if needs_quote(word) else escaped 67d9cf8291SDaniel Hwang 68d9cf8291SDaniel Hwang return " ".join([escape(arg) for arg in command]) 69d9cf8291SDaniel Hwang 70d9cf8291SDaniel Hwang 71d9cf8291SDaniel Hwangdef decode(string): 72d9cf8291SDaniel Hwang """Takes a command string and returns as a list.""" 73d9cf8291SDaniel Hwang 74d9cf8291SDaniel Hwang def unescape(arg): 75d9cf8291SDaniel Hwang """Gets rid of the escaping characters.""" 76d9cf8291SDaniel Hwang 77d9cf8291SDaniel Hwang if len(arg) >= 2 and arg[0] == arg[-1] and arg[0] == '"': 78d9cf8291SDaniel Hwang arg = arg[1:-1] 79*dd3c26a0STobias Hieta return re.sub(r'\\(["\\])', r"\1", arg) 80*dd3c26a0STobias Hieta return re.sub(r"\\([\\ $%&\(\)\[\]\{\}\*|<>@?!])", r"\1", arg) 81d9cf8291SDaniel Hwang 82d9cf8291SDaniel Hwang return [unescape(arg) for arg in shlex.split(string)] 83