chiark / gitweb /
fshash.in: Implement the `string_escape' conversion explicitly.
authorMark Wooding <mdw@distorted.org.uk>
Sat, 1 Jun 2024 03:27:54 +0000 (04:27 +0100)
committerMark Wooding <mdw@distorted.org.uk>
Sun, 9 Jun 2024 11:10:59 +0000 (12:10 +0100)
It's not available in Python 3.

fshash.in

index ad6ff58bf8c447a30d6b4bc645dc299ac22cb654..75d004f777b9a79fe54856b70cb3442927c0ebf1 100644 (file)
--- a/fshash.in
+++ b/fshash.in
@@ -41,8 +41,11 @@ VERSION = '@VERSION@'
 ###--------------------------------------------------------------------------
 ### Utilities.
 
+from cStringIO import StringIO; BytesIO = StringIO
 def bin(x): return x
 def text(x): return x
+def bytechr(x): return chr(x)
+def byteord(x): return ord(x)
 def excval(): return exc_info()[1]
 
 QUIS = OS.path.basename(argv[0])
@@ -60,6 +63,44 @@ def syserr(msg):
   moan(msg)
   SYSERR += 1
 
+def escapify(x):
+  out = StringIO()
+  for ch in bin(x):
+    k = byteord(ch)
+    if k == 9: out.write("\\t")
+    elif k == 10: out.write("\\n")
+    elif k == 13: out.write("\\r")
+    elif k == 39: out.write("\\'")
+    elif k == 92: out.write("\\\\")
+    elif 20 <= k <= 126: out.write(chr(k))
+    else: out.write("\\x%02x" % k)
+  return out.getvalue()
+
+R_STRESC = RX.compile(r"\\ (?: x ([0-9A-Fa-f]{2}) | (.))",
+                      RX.VERBOSE)
+def unescapify(x):
+  str = BytesIO()
+  i, n = 0, len(x)
+  while True:
+    m = R_STRESC.search(x, i)
+    if m is not None: j = m.start(0)
+    else: j = n
+    str.write(bin(str[i:j]))
+    if m is None: break
+    k, e = m.group(1), m.group(2)
+    if k is not None: ch = int(k, 16)
+    elif ch == "a": ch = 7
+    elif ch == "b": ch = 8
+    elif ch == "f": ch = 12
+    elif ch == "n": ch = 10
+    elif ch == "r": ch = 13
+    elif ch == "t": ch = 9
+    elif ch == "v": ch = 11
+    else: ch = byteord(e)
+    str.write(bytechr(ch))
+    i = m.end(0)
+  return text(out.getvalue())
+
 ###--------------------------------------------------------------------------
 ### File system enumeration.
 
@@ -364,7 +405,7 @@ class GenericFormatter (object):
     tm = T.gmtime(t)
     return T.strftime('%Y-%m-%dT%H:%M:%SZ', tm)
   def _enc_name(me, n):
-    return ' \\-> '.join(n.encode('string_escape').split(' -> '))
+    return ' \\-> '.join(escapify(n).split(' -> '))
   def name(me):
     return me._enc_name(me.fi.name)
   def info(me):
@@ -501,8 +542,8 @@ def clear_entry(db, lno, line):
       moan("failed to parse file entry (name split; line %d)" % lno)
       return False
     name, target = nn
-    target = target.decode('string_escape')
-  name = name.decode('string_escape')
+    target = unescapify(target)
+  name = unescapify(name)
 
   try:
     st = OS.lstat(name)