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