scripts: Added punescape repetition + nesting

Maybe a bit overkill, but I needed more flexibility around adding
arbitrary whitespace.

New modifiers:

  %s                    A space
  %{                    Start a substring
  %}s                   End and format a subtring
  %aaa[mod]             Repeat this mod aaa times

With this, it's easy to add arbitrary spaces:

  "%8s" -> "        "

Or equivalently:

  "%8{ %}s" -> "        "

Which allows arbitrary repitition of any substring:

  "%4{hi!%}s" -> "hi!hi!hi!hi!"

This substring modifier includes its own format string, which allows for
some really nice padding normally impossible in printf:

  "%{%(a)d/%(b)d%}8s" % {a:1,b:2}   -> "     1/2"
  "%{%(a)d/%(b)d%}8s" % {a:12,b:34} -> "   12/34"
This commit is contained in:
Christopher Haster
2026-02-19 03:07:54 -06:00
parent 526ab0148a
commit 97470c9930
10 changed files with 547 additions and 360 deletions
+56 -33
View File
@@ -1712,19 +1712,45 @@ def si2(x):
# attrs can override __getitem__ for lazy attr generation
def punescape(s, attrs=None):
pattern = re.compile(
'%[%n]'
'|' '%x..'
'|' '%u....'
'|' '%U........'
'|' '%\((?P<field>[^)]*)\)'
'(?P<format>[+\- #0-9\.]*[siIdboxXfFeEgG])')
'%' '(?P<rep>[0-9]*)' '(?P<pun>'
'[%ns]'
'|' 'x..'
'|' 'u....'
'|' 'U........'
'|' '\((?P<field>[^)]*)\)'
'(?P<format>[+\- #0-9\.]*[siIdboxXfFeEgG])'
'|' '\{(?P<substring>(?:[^%]|%[^}])*)%\}'
'(?P<subformat>[+\- #0-9\.]*[siIdboxXfFeEgG])' ')')
def format(f, v):
if f[-1] in 'dboxX':
if isinstance(v, str):
v = dat(v, 0)
v = int(v)
elif f[-1] in 'iIfFeEgG':
if isinstance(v, str):
v = dat(v, 0)
v = float(v)
if f[-1] in 'iI':
v = (si if 'i' in f[-1] else si2)(v)
f = f.replace('i', 's').replace('I', 's')
if '+' in f and not v.startswith('-'):
v = '+'+v
f = f.replace('+', '').replace('-', '')
else:
f = ('<' if '-' in f else '>') + f.replace('-', '')
v = str(v)
# note we need Python's new format syntax for binary
return ('{:%s}' % f).format(v)
def unescape(m):
if m.group()[1] == '%': return '%'
elif m.group()[1] == 'n': return '\n'
elif m.group()[1] == 'x': return chr(int(m.group()[2:], 16))
elif m.group()[1] == 'u': return chr(int(m.group()[2:], 16))
elif m.group()[1] == 'U': return chr(int(m.group()[2:], 16))
elif m.group()[1] == '(':
if m.group('pun')[0] == '%': s = '%'
elif m.group('pun')[0] == 'n': s = '\n'
elif m.group('pun')[0] == 's': s = ' '
elif m.group('pun')[0] == 'x': s = chr(int(m.group('pun')[1:], 16))
elif m.group('pun')[0] == 'u': s = chr(int(m.group('pun')[1:], 16))
elif m.group('pun')[0] == 'U': s = chr(int(m.group('pun')[1:], 16))
elif m.group('pun')[0] == '(':
if attrs is not None:
try:
v = attrs[m.group('field')]
@@ -1732,27 +1758,17 @@ def punescape(s, attrs=None):
return m.group()
else:
return m.group()
f = m.group('format')
if f[-1] in 'dboxX':
if isinstance(v, str):
v = dat(v, 0)
v = int(v)
elif f[-1] in 'iIfFeEgG':
if isinstance(v, str):
v = dat(v, 0)
v = float(v)
if f[-1] in 'iI':
v = (si if 'i' in f[-1] else si2)(v)
f = f.replace('i', 's').replace('I', 's')
if '+' in f and not v.startswith('-'):
v = '+'+v
f = f.replace('+', '').replace('-', '')
else:
f = ('<' if '-' in f else '>') + f.replace('-', '')
v = str(v)
# note we need Python's new format syntax for binary
return ('{:%s}' % f).format(v)
else: assert False
s = format(m.group('format'), v)
elif m.group('pun')[0] == '{':
v = punescape(m.group('substring'), attrs)
s = format(m.group('subformat'), v)
else:
return m.group()
if m.group('rep'):
s = int(m.group('rep'), 10) * s
return s
return re.sub(pattern, unescape, s)
@@ -1769,6 +1785,7 @@ def punescape_help():
print('mods:')
print(' %-21s %s' % ('%%', 'A literal % character'))
print(' %-21s %s' % ('%n', 'A newline'))
print(' %-21s %s' % ('%s', 'A space'))
print(' %-21s %s' % (
'%xaa', 'A character with the hex value aa'))
print(' %-21s %s' % (
@@ -1785,6 +1802,12 @@ def punescape_help():
'%(field)[dboxX]', 'An existing field formatted as an integer'))
print(' %-21s %s' % (
'%(field)[fFeEgG]', 'An existing field formatted as a float'))
print(' %-21s %s' % (
'%{', 'Start a substring'))
print(' %-21s %s' % (
'%}s', 'End and format a subtring'))
print(' %-21s %s' % (
'%aaa[mod]', 'Repeat this mod aaa times'))
# open with '-' for stdin/stdout