Skip to content

Instantly share code, notes, and snippets.

@jcommelin
Created October 9, 2018 12:11
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save jcommelin/ab7b99ee1dcd9084e2f73a940e91bb40 to your computer and use it in GitHub Desktop.
Save jcommelin/ab7b99ee1dcd9084e2f73a940e91bb40 to your computer and use it in GitHub Desktop.
Apply replacement script on file
#!/bin/python3
### Read a replacement script from stdin
### Read a filename from commandline args
### Output to stdout
### Replacement script has the following form
# ----eofmarker
# line:col:line:col
# multiline replacement
# text foobar quux
# eofmarker
# ----anothereof
# line:col:line:col
# etc etc etc
# anothereof
### The eofmarker can by any string (without newlines)
### The final newline of the replacement text is stripped.
### line:col:line:col are the coordinates
### of the begin and end of replacement
import sys
import functools
def eprint(msg):
print(msg, file=sys.stderr)
sys.exit(1)
scripts = []
cur = {}
cur['lines'] = []
status = 0
for nr,line in enumerate(sys.stdin.readlines(),start=1):
line = line.rstrip()
if (status == 0): # at beginning of new chunk
if (line[0:4] == "----"):
cur['eof'] = line[4:]
status = 1
else:
eprint("Illegal start of chunk in replacement script at line: " + str(nr))
elif (status == 1): # parsing line and column numbers
try:
[sl1,sc1,sl2,sc2] = line.split(':')
(l1,c1,l2,c2) = (int(sl1),int(sc1),int(sl2),int(sc2))
assert [sl1,sc1,sl2,sc2] == [str(l1),str(c1),str(l2),str(c2)]
assert l1 < l2 or (l1 == l2 and c1 <= c2)
cur['coords'] = (l1,c1,l2,c2)
except:
eprint("Illegal location line in replacement script at line: " + str(nr))
status = 2
elif (status == 2):
if (line == cur['eof']):
if len(cur['lines']) > 0: # drop newline of final replacement string
cur['lines'][-1] = cur['lines'][-1][:-1]
scripts += [cur]
cur = {}
cur['lines'] = []
status = 0
else:
cur['lines'] += line + '\n'
else: # shouldn't happen
eprint("Unreachable code was reached")
def subst(text,script):
(l1,c1,l2,c2) = script['coords']
lines = script['lines']
for nr,line in enumerate(text,start=1):
if nr < l1:
yield line
elif nr == l1:
if c1 > len(line):
eprint("Line is too short. Line number: " + str(nr))
if len(lines) > 0:
lines[0] = line[0:c1] + lines[0]
else:
lines = [line[0:c1]]
if nr == l2:
if c2 > len(line):
eprint("Line is too short. Line number: " + str(nr))
lines[-1] += line[c2:]
for l in lines:
yield l
elif nr > l2:
yield line
if nr < l1:
eprint("Replacement failed. File is too short.")
with open(sys.argv[1],"r+") as f:
for line in functools.reduce(subst,reversed(scripts),f.readlines()):
print(line,end='')
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment