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