#!/usr/bin/env python3
import os
import sys
import shlex
from subprocess import Popen, PIPE
symbol = '@'
encoding = 'utf-8'
iterations = 1
input_file = '/dev/stdin'
output_file = '/dev/stdout'
unshebang = False
args = sys.argv
# If the file is executed, the first command line argument will be the first argument in the shebang,
# the second will be the rest of the arguments in the command line as is and the third and final will
# be the executed file.
if len(args) == 3:
if os.path.exists(args[2]) and os.path.isfile(args[2]) and ((os.stat(args[2]).st_mode & 0o111) != 0):
args[1 : 2] = shlex.split(args[1])
for i in range(1, len(args)):
arg = args[i]
i += 1
if arg in ('-s', '--symbol'): symbol = sys.argv[i]
elif arg in ('-e', '--encoding'): encoding = sys.argv[i]
elif arg in ('-n', '--iterations'): iterations = int(sys.argv[i])
elif arg in ('-u', '--unshebang'): unshebang = True; continue
elif arg in ('-i', '--input'): input_file = sys.argv[i]
elif arg in ('-o', '--output'): output_file = sys.argv[i]
elif arg in ('-f', '--file'):
input_file = sys.argv[i]
output_file = sys.argv[i]
else:
continue
i += 1
if input_file == '-': input_file = '/dev/stdin'
if output_file == '-': output_file = '/dev/stdout'
if iterations < 1:
if input_file != output_file:
data = None
with open(input_file, 'rb') as file:
data = file.read()
with open(write_file, 'wb') as file:
file.write(data)
file.flush()
sys.exit(0)
data = None
with open(input_file, 'rb') as file:
data = file.read().decode(encoding, 'error').split('\n')
if unshebang:
if data[0][:2] == '#!':
data[:] = data[1:]
def pp(line):
rc = ''
symb = False
brackets = 0
esc = False
dollar = False
quote = []
for c in line:
if brackets > 0:
if esc:
esc = False
elif (c == ')') or (c == '}'):
brackets -= 1
if brackets == 0:
rc += c + '"\''
continue
elif (c == '(') or (c == '{'):
brackets += 1
elif c == '\\':
esc = True
rc += c
elif symb:
symb = False
if (c == '(') or (c == '{'):
brackets += 1
rc += '\'"$' + c
else:
rc += c
elif c == symbol:
symb = True
elif len(quote) > 0:
if esc:
esc = False
elif dollar:
dollar = False
if c == '(':
quote.append(')')
elif c == '{':
quote.append('}')
elif c == quote[-1]:
quote[:] = quote[:-1]
elif (quote[-1] in ')}') and (c in '\'"`'):
quote.append(c)
elif (c == '\\') and (quote[-1] != "'"):
esc = True
elif c == '$':
dollar = True
rc += c
elif (c == '"') or (c == "'") or (c == '`'):
quote.append(c)
rc += c
else:
rc += c
return rc
for _ in range(iterations):
entered = False
bashed = []
for lineno in range(len(data)):
line = data[lineno]
if line.startswith(symbol + '<'):
bashed.append(line[2:])
entered = True
elif line.startswith(symbol + '>'):
bashed.append(line[2:])
entered = False
elif entered:
bashed.append(line)
else:
line = '\'%s\'' % line.replace('\'', '\'\\\'\'')
bashed.append('echo $\'\\e%i\\e\'%s' % (lineno, pp(line)))
bashed = '\n'.join(bashed).encode(encoding)
bash = Popen(["bash"], stdin = PIPE, stdout = PIPE, stderr = sys.stderr)
bashed = bash.communicate(bashed)[0]
if bash.returncode != 0:
sys.exit(bash.returncode)
bashed = bashed.decode(encoding, 'error').split('\n')
data = []
lineno = -1
for line in bashed:
no = -1
if line.startswith('\033'):
no = int(line[1:].split('\033')[0])
line = '\033'.join(line[1:].split('\033')[1:])
if no > lineno:
while no != lineno + 1:
data.append('')
lineno += 1
data.append(line)
lineno += 1
with open(output_file, 'wb') as file:
file.write('\n'.join(data).encode(encoding))
file.flush()