aboutsummaryrefslogtreecommitdiffstats
path: root/contrib/tools/python3/src/Lib/quopri.py
diff options
context:
space:
mode:
authorDevtools Arcadia <arcadia-devtools@yandex-team.ru>2022-02-07 18:08:42 +0300
committerDevtools Arcadia <arcadia-devtools@mous.vla.yp-c.yandex.net>2022-02-07 18:08:42 +0300
commit1110808a9d39d4b808aef724c861a2e1a38d2a69 (patch)
treee26c9fed0de5d9873cce7e00bc214573dc2195b7 /contrib/tools/python3/src/Lib/quopri.py
downloadydb-1110808a9d39d4b808aef724c861a2e1a38d2a69.tar.gz
intermediate changes
ref:cde9a383711a11544ce7e107a78147fb96cc4029
Diffstat (limited to 'contrib/tools/python3/src/Lib/quopri.py')
-rw-r--r--contrib/tools/python3/src/Lib/quopri.py242
1 files changed, 242 insertions, 0 deletions
diff --git a/contrib/tools/python3/src/Lib/quopri.py b/contrib/tools/python3/src/Lib/quopri.py
new file mode 100644
index 0000000000..08899c5cb7
--- /dev/null
+++ b/contrib/tools/python3/src/Lib/quopri.py
@@ -0,0 +1,242 @@
+#! /usr/bin/env python3
+
+"""Conversions to/from quoted-printable transport encoding as per RFC 1521."""
+
+# (Dec 1991 version).
+
+__all__ = ["encode", "decode", "encodestring", "decodestring"]
+
+ESCAPE = b'='
+MAXLINESIZE = 76
+HEX = b'0123456789ABCDEF'
+EMPTYSTRING = b''
+
+try:
+ from binascii import a2b_qp, b2a_qp
+except ImportError:
+ a2b_qp = None
+ b2a_qp = None
+
+
+def needsquoting(c, quotetabs, header):
+ """Decide whether a particular byte ordinal needs to be quoted.
+
+ The 'quotetabs' flag indicates whether embedded tabs and spaces should be
+ quoted. Note that line-ending tabs and spaces are always encoded, as per
+ RFC 1521.
+ """
+ assert isinstance(c, bytes)
+ if c in b' \t':
+ return quotetabs
+ # if header, we have to escape _ because _ is used to escape space
+ if c == b'_':
+ return header
+ return c == ESCAPE or not (b' ' <= c <= b'~')
+
+def quote(c):
+ """Quote a single character."""
+ assert isinstance(c, bytes) and len(c)==1
+ c = ord(c)
+ return ESCAPE + bytes((HEX[c//16], HEX[c%16]))
+
+
+
+def encode(input, output, quotetabs, header=False):
+ """Read 'input', apply quoted-printable encoding, and write to 'output'.
+
+ 'input' and 'output' are binary file objects. The 'quotetabs' flag
+ indicates whether embedded tabs and spaces should be quoted. Note that
+ line-ending tabs and spaces are always encoded, as per RFC 1521.
+ The 'header' flag indicates whether we are encoding spaces as _ as per RFC
+ 1522."""
+
+ if b2a_qp is not None:
+ data = input.read()
+ odata = b2a_qp(data, quotetabs=quotetabs, header=header)
+ output.write(odata)
+ return
+
+ def write(s, output=output, lineEnd=b'\n'):
+ # RFC 1521 requires that the line ending in a space or tab must have
+ # that trailing character encoded.
+ if s and s[-1:] in b' \t':
+ output.write(s[:-1] + quote(s[-1:]) + lineEnd)
+ elif s == b'.':
+ output.write(quote(s) + lineEnd)
+ else:
+ output.write(s + lineEnd)
+
+ prevline = None
+ while 1:
+ line = input.readline()
+ if not line:
+ break
+ outline = []
+ # Strip off any readline induced trailing newline
+ stripped = b''
+ if line[-1:] == b'\n':
+ line = line[:-1]
+ stripped = b'\n'
+ # Calculate the un-length-limited encoded line
+ for c in line:
+ c = bytes((c,))
+ if needsquoting(c, quotetabs, header):
+ c = quote(c)
+ if header and c == b' ':
+ outline.append(b'_')
+ else:
+ outline.append(c)
+ # First, write out the previous line
+ if prevline is not None:
+ write(prevline)
+ # Now see if we need any soft line breaks because of RFC-imposed
+ # length limitations. Then do the thisline->prevline dance.
+ thisline = EMPTYSTRING.join(outline)
+ while len(thisline) > MAXLINESIZE:
+ # Don't forget to include the soft line break `=' sign in the
+ # length calculation!
+ write(thisline[:MAXLINESIZE-1], lineEnd=b'=\n')
+ thisline = thisline[MAXLINESIZE-1:]
+ # Write out the current line
+ prevline = thisline
+ # Write out the last line, without a trailing newline
+ if prevline is not None:
+ write(prevline, lineEnd=stripped)
+
+def encodestring(s, quotetabs=False, header=False):
+ if b2a_qp is not None:
+ return b2a_qp(s, quotetabs=quotetabs, header=header)
+ from io import BytesIO
+ infp = BytesIO(s)
+ outfp = BytesIO()
+ encode(infp, outfp, quotetabs, header)
+ return outfp.getvalue()
+
+
+
+def decode(input, output, header=False):
+ """Read 'input', apply quoted-printable decoding, and write to 'output'.
+ 'input' and 'output' are binary file objects.
+ If 'header' is true, decode underscore as space (per RFC 1522)."""
+
+ if a2b_qp is not None:
+ data = input.read()
+ odata = a2b_qp(data, header=header)
+ output.write(odata)
+ return
+
+ new = b''
+ while 1:
+ line = input.readline()
+ if not line: break
+ i, n = 0, len(line)
+ if n > 0 and line[n-1:n] == b'\n':
+ partial = 0; n = n-1
+ # Strip trailing whitespace
+ while n > 0 and line[n-1:n] in b" \t\r":
+ n = n-1
+ else:
+ partial = 1
+ while i < n:
+ c = line[i:i+1]
+ if c == b'_' and header:
+ new = new + b' '; i = i+1
+ elif c != ESCAPE:
+ new = new + c; i = i+1
+ elif i+1 == n and not partial:
+ partial = 1; break
+ elif i+1 < n and line[i+1:i+2] == ESCAPE:
+ new = new + ESCAPE; i = i+2
+ elif i+2 < n and ishex(line[i+1:i+2]) and ishex(line[i+2:i+3]):
+ new = new + bytes((unhex(line[i+1:i+3]),)); i = i+3
+ else: # Bad escape sequence -- leave it in
+ new = new + c; i = i+1
+ if not partial:
+ output.write(new + b'\n')
+ new = b''
+ if new:
+ output.write(new)
+
+def decodestring(s, header=False):
+ if a2b_qp is not None:
+ return a2b_qp(s, header=header)
+ from io import BytesIO
+ infp = BytesIO(s)
+ outfp = BytesIO()
+ decode(infp, outfp, header=header)
+ return outfp.getvalue()
+
+
+
+# Other helper functions
+def ishex(c):
+ """Return true if the byte ordinal 'c' is a hexadecimal digit in ASCII."""
+ assert isinstance(c, bytes)
+ return b'0' <= c <= b'9' or b'a' <= c <= b'f' or b'A' <= c <= b'F'
+
+def unhex(s):
+ """Get the integer value of a hexadecimal number."""
+ bits = 0
+ for c in s:
+ c = bytes((c,))
+ if b'0' <= c <= b'9':
+ i = ord('0')
+ elif b'a' <= c <= b'f':
+ i = ord('a')-10
+ elif b'A' <= c <= b'F':
+ i = ord(b'A')-10
+ else:
+ assert False, "non-hex digit "+repr(c)
+ bits = bits*16 + (ord(c) - i)
+ return bits
+
+
+
+def main():
+ import sys
+ import getopt
+ try:
+ opts, args = getopt.getopt(sys.argv[1:], 'td')
+ except getopt.error as msg:
+ sys.stdout = sys.stderr
+ print(msg)
+ print("usage: quopri [-t | -d] [file] ...")
+ print("-t: quote tabs")
+ print("-d: decode; default encode")
+ sys.exit(2)
+ deco = False
+ tabs = False
+ for o, a in opts:
+ if o == '-t': tabs = True
+ if o == '-d': deco = True
+ if tabs and deco:
+ sys.stdout = sys.stderr
+ print("-t and -d are mutually exclusive")
+ sys.exit(2)
+ if not args: args = ['-']
+ sts = 0
+ for file in args:
+ if file == '-':
+ fp = sys.stdin.buffer
+ else:
+ try:
+ fp = open(file, "rb")
+ except OSError as msg:
+ sys.stderr.write("%s: can't open (%s)\n" % (file, msg))
+ sts = 1
+ continue
+ try:
+ if deco:
+ decode(fp, sys.stdout.buffer)
+ else:
+ encode(fp, sys.stdout.buffer, tabs)
+ finally:
+ if file != '-':
+ fp.close()
+ if sts:
+ sys.exit(sts)
+
+
+
+if __name__ == '__main__':
+ main()