diff options
author | Devtools Arcadia <arcadia-devtools@yandex-team.ru> | 2022-02-07 18:08:42 +0300 |
---|---|---|
committer | Devtools Arcadia <arcadia-devtools@mous.vla.yp-c.yandex.net> | 2022-02-07 18:08:42 +0300 |
commit | 1110808a9d39d4b808aef724c861a2e1a38d2a69 (patch) | |
tree | e26c9fed0de5d9873cce7e00bc214573dc2195b7 /contrib/tools/python3/src/Lib/mailcap.py | |
download | ydb-1110808a9d39d4b808aef724c861a2e1a38d2a69.tar.gz |
intermediate changes
ref:cde9a383711a11544ce7e107a78147fb96cc4029
Diffstat (limited to 'contrib/tools/python3/src/Lib/mailcap.py')
-rw-r--r-- | contrib/tools/python3/src/Lib/mailcap.py | 276 |
1 files changed, 276 insertions, 0 deletions
diff --git a/contrib/tools/python3/src/Lib/mailcap.py b/contrib/tools/python3/src/Lib/mailcap.py new file mode 100644 index 0000000000..ae416a8e9f --- /dev/null +++ b/contrib/tools/python3/src/Lib/mailcap.py @@ -0,0 +1,276 @@ +"""Mailcap file handling. See RFC 1524.""" + +import os +import warnings + +__all__ = ["getcaps","findmatch"] + + +def lineno_sort_key(entry): + # Sort in ascending order, with unspecified entries at the end + if 'lineno' in entry: + return 0, entry['lineno'] + else: + return 1, 0 + + +# Part 1: top-level interface. + +def getcaps(): + """Return a dictionary containing the mailcap database. + + The dictionary maps a MIME type (in all lowercase, e.g. 'text/plain') + to a list of dictionaries corresponding to mailcap entries. The list + collects all the entries for that MIME type from all available mailcap + files. Each dictionary contains key-value pairs for that MIME type, + where the viewing command is stored with the key "view". + + """ + caps = {} + lineno = 0 + for mailcap in listmailcapfiles(): + try: + fp = open(mailcap, 'r') + except OSError: + continue + with fp: + morecaps, lineno = _readmailcapfile(fp, lineno) + for key, value in morecaps.items(): + if not key in caps: + caps[key] = value + else: + caps[key] = caps[key] + value + return caps + +def listmailcapfiles(): + """Return a list of all mailcap files found on the system.""" + # This is mostly a Unix thing, but we use the OS path separator anyway + if 'MAILCAPS' in os.environ: + pathstr = os.environ['MAILCAPS'] + mailcaps = pathstr.split(os.pathsep) + else: + if 'HOME' in os.environ: + home = os.environ['HOME'] + else: + # Don't bother with getpwuid() + home = '.' # Last resort + mailcaps = [home + '/.mailcap', '/etc/mailcap', + '/usr/etc/mailcap', '/usr/local/etc/mailcap'] + return mailcaps + + +# Part 2: the parser. +def readmailcapfile(fp): + """Read a mailcap file and return a dictionary keyed by MIME type.""" + warnings.warn('readmailcapfile is deprecated, use getcaps instead', + DeprecationWarning, 2) + caps, _ = _readmailcapfile(fp, None) + return caps + + +def _readmailcapfile(fp, lineno): + """Read a mailcap file and return a dictionary keyed by MIME type. + + Each MIME type is mapped to an entry consisting of a list of + dictionaries; the list will contain more than one such dictionary + if a given MIME type appears more than once in the mailcap file. + Each dictionary contains key-value pairs for that MIME type, where + the viewing command is stored with the key "view". + """ + caps = {} + while 1: + line = fp.readline() + if not line: break + # Ignore comments and blank lines + if line[0] == '#' or line.strip() == '': + continue + nextline = line + # Join continuation lines + while nextline[-2:] == '\\\n': + nextline = fp.readline() + if not nextline: nextline = '\n' + line = line[:-2] + nextline + # Parse the line + key, fields = parseline(line) + if not (key and fields): + continue + if lineno is not None: + fields['lineno'] = lineno + lineno += 1 + # Normalize the key + types = key.split('/') + for j in range(len(types)): + types[j] = types[j].strip() + key = '/'.join(types).lower() + # Update the database + if key in caps: + caps[key].append(fields) + else: + caps[key] = [fields] + return caps, lineno + +def parseline(line): + """Parse one entry in a mailcap file and return a dictionary. + + The viewing command is stored as the value with the key "view", + and the rest of the fields produce key-value pairs in the dict. + """ + fields = [] + i, n = 0, len(line) + while i < n: + field, i = parsefield(line, i, n) + fields.append(field) + i = i+1 # Skip semicolon + if len(fields) < 2: + return None, None + key, view, rest = fields[0], fields[1], fields[2:] + fields = {'view': view} + for field in rest: + i = field.find('=') + if i < 0: + fkey = field + fvalue = "" + else: + fkey = field[:i].strip() + fvalue = field[i+1:].strip() + if fkey in fields: + # Ignore it + pass + else: + fields[fkey] = fvalue + return key, fields + +def parsefield(line, i, n): + """Separate one key-value pair in a mailcap entry.""" + start = i + while i < n: + c = line[i] + if c == ';': + break + elif c == '\\': + i = i+2 + else: + i = i+1 + return line[start:i].strip(), i + + +# Part 3: using the database. + +def findmatch(caps, MIMEtype, key='view', filename="/dev/null", plist=[]): + """Find a match for a mailcap entry. + + Return a tuple containing the command line, and the mailcap entry + used; (None, None) if no match is found. This may invoke the + 'test' command of several matching entries before deciding which + entry to use. + + """ + entries = lookup(caps, MIMEtype, key) + # XXX This code should somehow check for the needsterminal flag. + for e in entries: + if 'test' in e: + test = subst(e['test'], filename, plist) + if test and os.system(test) != 0: + continue + command = subst(e[key], MIMEtype, filename, plist) + return command, e + return None, None + +def lookup(caps, MIMEtype, key=None): + entries = [] + if MIMEtype in caps: + entries = entries + caps[MIMEtype] + MIMEtypes = MIMEtype.split('/') + MIMEtype = MIMEtypes[0] + '/*' + if MIMEtype in caps: + entries = entries + caps[MIMEtype] + if key is not None: + entries = [e for e in entries if key in e] + entries = sorted(entries, key=lineno_sort_key) + return entries + +def subst(field, MIMEtype, filename, plist=[]): + # XXX Actually, this is Unix-specific + res = '' + i, n = 0, len(field) + while i < n: + c = field[i]; i = i+1 + if c != '%': + if c == '\\': + c = field[i:i+1]; i = i+1 + res = res + c + else: + c = field[i]; i = i+1 + if c == '%': + res = res + c + elif c == 's': + res = res + filename + elif c == 't': + res = res + MIMEtype + elif c == '{': + start = i + while i < n and field[i] != '}': + i = i+1 + name = field[start:i] + i = i+1 + res = res + findparam(name, plist) + # XXX To do: + # %n == number of parts if type is multipart/* + # %F == list of alternating type and filename for parts + else: + res = res + '%' + c + return res + +def findparam(name, plist): + name = name.lower() + '=' + n = len(name) + for p in plist: + if p[:n].lower() == name: + return p[n:] + return '' + + +# Part 4: test program. + +def test(): + import sys + caps = getcaps() + if not sys.argv[1:]: + show(caps) + return + for i in range(1, len(sys.argv), 2): + args = sys.argv[i:i+2] + if len(args) < 2: + print("usage: mailcap [MIMEtype file] ...") + return + MIMEtype = args[0] + file = args[1] + command, e = findmatch(caps, MIMEtype, 'view', file) + if not command: + print("No viewer found for", type) + else: + print("Executing:", command) + sts = os.system(command) + sts = os.waitstatus_to_exitcode(sts) + if sts: + print("Exit status:", sts) + +def show(caps): + print("Mailcap files:") + for fn in listmailcapfiles(): print("\t" + fn) + print() + if not caps: caps = getcaps() + print("Mailcap entries:") + print() + ckeys = sorted(caps) + for type in ckeys: + print(type) + entries = caps[type] + for e in entries: + keys = sorted(e) + for k in keys: + print(" %-15s" % k, e[k]) + print() + +if __name__ == '__main__': + test() |