| # Mailcap file handling. See RFC 1524. |
| |
| import os |
| import string |
| import tempfile |
| |
| |
| # Part 1: top-level interface. |
| |
| def getcaps(): |
| caps = {} |
| for mailcap in listmailcapfiles(): |
| try: |
| fp = open(mailcap, 'r') |
| except: |
| continue |
| morecaps = readmailcapfile(fp) |
| fp.close() |
| for key in morecaps.keys(): |
| if not caps.has_key(key): |
| caps[key] = morecaps[key] |
| else: |
| caps[key] = caps[key] + morecaps[key] |
| return caps |
| |
| def listmailcapfiles(): |
| # XXX Actually, this is Unix-specific |
| if os.environ.has_key('MAILCAPS'): |
| str = os.environ['MAILCAPS'] |
| mailcaps = string.splitfields(str, ':') |
| else: |
| if os.environ.has_key('HOME'): |
| 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): |
| caps = {} |
| while 1: |
| line = fp.readline() |
| if not line: break |
| # Ignore comments and blank lines |
| if line[0] == '#' or string.strip(line) == '': |
| 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): |
| cotinue |
| # Normalize the key |
| types = string.splitfields(key, '/') |
| for j in range(len(types)): |
| types[j] = string.strip(types[j]) |
| key = string.lower(string.joinfields(types, '/')) |
| # Update the database |
| if caps.has_key(key): |
| caps[key].append(fields) |
| else: |
| caps[key] = [fields] |
| return caps |
| |
| def parseline(line): |
| 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 = string.find(field, '=') |
| if i < 0: |
| fkey = field |
| fvalue = "" |
| else: |
| fkey = string.strip(field[:i]) |
| fvalue = string.strip(field[i+1:]) |
| if fields.has_key(fkey): |
| # Ignore it |
| pass |
| else: |
| fields[fkey] = fvalue |
| return key, fields |
| |
| def parsefield(line, i, n): |
| start = i |
| while i < n: |
| c = line[i] |
| if c == ';': |
| break |
| elif c == '\\': |
| i = i+2 |
| else: |
| i = i+1 |
| return string.strip(line[start:i]), i |
| |
| |
| # Part 3: using the database. |
| |
| def findmatch(caps, type, key='view', filename="/dev/null", plist=[]): |
| entries = lookup(caps, type, key) |
| for e in entries: |
| if e.has_key('test'): |
| test = subst(e['test'], filename, plist) |
| if test and os.system(test) != 0: |
| continue |
| command = subst(e[key], type, filename, plist) |
| return command, e |
| return None, None |
| |
| def lookup(caps, type, key=None): |
| entries = [] |
| if caps.has_key(type): |
| entries = entries + caps[type] |
| types = string.splitfields(type, '/') |
| type = types[0] + '/*' |
| if caps.has_key(type): |
| entries = entries + caps[type] |
| if key is not None: |
| entries = filter(lambda e, key=key: e.has_key(key), entries) |
| return entries |
| |
| def subst(field, type, 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 + type |
| 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 = string.lower(name) + '=' |
| n = len(name) |
| for p in plist: |
| if string.lower(p[:n]) == 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 [type file] ..." |
| return |
| type = args[0] |
| file = args[1] |
| command, e = findmatch(caps, type, 'view', file) |
| if not command: |
| print "No viewer found for", type |
| else: |
| print "Executing:", command |
| sts = os.system(command) |
| 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 = caps.keys() |
| ckeys.sort() |
| for type in ckeys: |
| print type |
| entries = caps[type] |
| for e in entries: |
| keys = e.keys() |
| keys.sort() |
| for k in keys: |
| print " %-15s" % k, e[k] |
| print |
| |
| if __name__ == '__main__': |
| test() |