# -*- coding: utf-8 -*- # Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. # See https://llvm.org/LICENSE.txt for license information. # SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception """ This module implements basic shell escaping/unescaping methods. """ import re import shlex __all__ = ["encode", "decode"] def encode(command): """Takes a command as list and returns a string.""" def needs_quote(word): """Returns true if arguments needs to be protected by quotes. Previous implementation was shlex.split method, but that's not good for this job. Currently is running through the string with a basic state checking.""" reserved = { " ", "$", "%", "&", "(", ")", "[", "]", "{", "}", "*", "|", "<", ">", "@", "?", "!", } state = 0 for current in word: if state == 0 and current in reserved: return True elif state == 0 and current == "\\": state = 1 elif state == 1 and current in reserved | {"\\"}: state = 0 elif state == 0 and current == '"': state = 2 elif state == 2 and current == '"': state = 0 elif state == 0 and current == "'": state = 3 elif state == 3 and current == "'": state = 0 return state != 0 def escape(word): """Do protect argument if that's needed.""" table = {"\\": "\\\\", '"': '\\"'} escaped = "".join([table.get(c, c) for c in word]) return '"' + escaped + '"' if needs_quote(word) else escaped return " ".join([escape(arg) for arg in command]) def decode(string): """Takes a command string and returns as a list.""" def unescape(arg): """Gets rid of the escaping characters.""" if len(arg) >= 2 and arg[0] == arg[-1] and arg[0] == '"': arg = arg[1:-1] return re.sub(r'\\(["\\])', r"\1", arg) return re.sub(r"\\([\\ $%&\(\)\[\]\{\}\*|<>@?!])", r"\1", arg) return [unescape(arg) for arg in shlex.split(string)]