patchupdate.py: Use printf instead of echo to workaround bash/dash incompatibilities.

This commit is contained in:
Sebastian Lackner 2017-01-19 09:15:23 +01:00
parent c0993595fe
commit 76a233502e
3 changed files with 1147 additions and 1083 deletions

File diff suppressed because it is too large Load Diff

View File

@ -3,7 +3,7 @@
#
# Automatic patch dependency checker and apply script generator.
#
# Copyright (C) 2014-2016 Sebastian Lackner
# Copyright (C) 2014-2017 Sebastian Lackner
# Copyright (C) 2015 Michael Müller
#
# This library is free software; you can redistribute it and/or
@ -21,6 +21,7 @@
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
#
from patchutils import escape_sh, escape_c
import argparse
import binascii
import cPickle as pickle
@ -113,10 +114,6 @@ def _split_seq(iterable, size):
yield items
items = list(itertools.islice(it, size))
def _escape(s):
"""Escape string inside of '...' quotes."""
return s.replace("\\", "\\\\\\\\").replace("\"", "\\\"").replace("'", "'\\''")
def _load_dict(filename):
"""Load a Python dictionary object from a file."""
try:
@ -753,8 +750,8 @@ def generate_script(all_patches, resolved):
lines.append("\t(\n")
for p in _unique(patch.patches, key=lambda p: (p.patch_author, p.patch_subject, p.patch_revision)):
if p.patch_author is None: continue
lines.append("\t\techo '+ { \"%s\", \"%s\", %d },';\n" %
(_escape(p.patch_author), _escape(p.patch_subject), p.patch_revision))
lines.append("\t\tprintf '%%s\\n' '+ { \"%s\", \"%s\", %d },';\n" %
(escape_sh(escape_c(p.patch_author)), escape_sh(escape_c(p.patch_subject)), p.patch_revision))
lines.append("\t) >> \"$patchlist\"\n")
lines.append("fi\n\n")
lines_apply = lines

View File

@ -2,7 +2,7 @@
#
# Python functions to read, split and apply patches.
#
# Copyright (C) 2014-2016 Sebastian Lackner
# Copyright (C) 2014-2017 Sebastian Lackner
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
@ -27,6 +27,7 @@ import itertools
import os
import re
import shutil
import stat
import subprocess
import sys
import tempfile
@ -665,6 +666,14 @@ def generate_ifdef_patch(original, patched, ifdef):
# Return the final diff
return diff
def escape_sh(text):
"""Escape string for usage in shell '...' quotes."""
return text.replace("'", "'\\''")
def escape_c(text):
"""Escape string for usage in C "..." quotes."""
return text.replace("\\", "\\\\").replace("\"", "\\\"")
if __name__ == "__main__":
import unittest
@ -948,4 +957,62 @@ if __name__ == "__main__":
lines = diff.read().rstrip("\n").split("\n")
self.assertEqual(lines, expected)
# Basic tests for escape_sh()
class EscapeShellTests(unittest.TestCase):
ascii = "".join([chr(i) for i in range(32, 127)] + \
["\\" + chr(i) for i in range(32, 127)])
def test_bash(self):
script = tempfile.NamedTemporaryFile(mode='w+', delete=False)
try:
script.write("#!/bin/bash\nprintf '%%s\\n' '%s'\n" % escape_sh(self.ascii))
script.close()
st = os.stat(script.name)
os.chmod(script.name, st.st_mode | stat.S_IEXEC)
result = subprocess.check_output([script.name])
self.assertEqual(result.rstrip("\n"), self.ascii)
finally:
os.unlink(script.name)
@unittest.skipUnless(os.path.exists("/bin/dash"), "requires Dash")
def test_dash(self):
script = tempfile.NamedTemporaryFile(mode='w+', delete=False)
try:
script.write("#!/bin/dash\nprintf '%%s\\n' '%s'\n" % escape_sh(self.ascii))
script.close()
st = os.stat(script.name)
os.chmod(script.name, st.st_mode | stat.S_IEXEC)
result = subprocess.check_output([script.name])
self.assertEqual(result.rstrip("\n"), self.ascii)
finally:
os.unlink(script.name)
# Basic tests for escape_c()
class EscapeCTests(unittest.TestCase):
ascii = "".join([chr(i) for i in range(32, 127)] + \
["\\" + chr(i) for i in range(32, 127)])
def test_c(self):
source = tempfile.NamedTemporaryFile(mode='w+', suffix='.c', delete=False)
try:
source.write("#include <stdio.h>\n")
source.write("int main() { printf(\"%%s\", \"%s\"); return 0; }\n" % escape_c(self.ascii))
source.close()
compiled = tempfile.NamedTemporaryFile(mode='w+', delete=False)
try:
compiled.close()
subprocess.call(["gcc", source.name, "-o", compiled.name])
result = subprocess.check_output([compiled.name])
self.assertEqual(result.rstrip("\n"), self.ascii)
finally:
os.unlink(compiled.name)
finally:
os.unlink(source.name)
unittest.main()