1 """Mailcap file handling. See RFC 1524."""
5 __all__ = ["getcaps","findmatch"]
7 # Part 1: top-level interface.
10 """Return a dictionary containing the mailcap database.
12 The dictionary maps a MIME type (in all lowercase, e.g. 'text/plain')
13 to a list of dictionaries corresponding to mailcap entries. The list
14 collects all the entries for that MIME type from all available mailcap
15 files. Each dictionary contains key-value pairs for that MIME type,
16 where the viewing command is stored with the key "view".
20 for mailcap in listmailcapfiles():
22 fp = open(mailcap, 'r')
25 morecaps = readmailcapfile(fp)
27 for key, value in morecaps.iteritems():
31 caps[key] = caps[key] + value
34 def listmailcapfiles():
35 """Return a list of all mailcap files found on the system."""
36 # XXX Actually, this is Unix-specific
37 if 'MAILCAPS' in os.environ:
38 str = os.environ['MAILCAPS']
39 mailcaps = str.split(':')
41 if 'HOME' in os.environ:
42 home = os.environ['HOME']
44 # Don't bother with getpwuid()
45 home = '.' # Last resort
46 mailcaps = [home + '/.mailcap', '/etc/mailcap',
47 '/usr/etc/mailcap', '/usr/local/etc/mailcap']
53 def readmailcapfile(fp):
54 """Read a mailcap file and return a dictionary keyed by MIME type.
56 Each MIME type is mapped to an entry consisting of a list of
57 dictionaries; the list will contain more than one such dictionary
58 if a given MIME type appears more than once in the mailcap file.
59 Each dictionary contains key-value pairs for that MIME type, where
60 the viewing command is stored with the key "view".
66 # Ignore comments and blank lines
67 if line[0] == '#' or line.strip() == '':
70 # Join continuation lines
71 while nextline[-2:] == '\\\n':
72 nextline = fp.readline()
73 if not nextline: nextline = '\n'
74 line = line[:-2] + nextline
76 key, fields = parseline(line)
77 if not (key and fields):
80 types = key.split('/')
81 for j in range(len(types)):
82 types[j] = types[j].strip()
83 key = '/'.join(types).lower()
86 caps[key].append(fields)
92 """Parse one entry in a mailcap file and return a dictionary.
94 The viewing command is stored as the value with the key "view",
95 and the rest of the fields produce key-value pairs in the dict.
100 field, i = parsefield(line, i, n)
102 i = i+1 # Skip semicolon
105 key, view, rest = fields[0], fields[1], fields[2:]
106 fields = {'view': view}
113 fkey = field[:i].strip()
114 fvalue = field[i+1:].strip()
119 fields[fkey] = fvalue
122 def parsefield(line, i, n):
123 """Separate one key-value pair in a mailcap entry."""
133 return line[start:i].strip(), i
136 # Part 3: using the database.
138 def findmatch(caps, MIMEtype, key='view', filename="/dev/null", plist=[]):
139 """Find a match for a mailcap entry.
141 Return a tuple containing the command line, and the mailcap entry
142 used; (None, None) if no match is found. This may invoke the
143 'test' command of several matching entries before deciding which
147 entries = lookup(caps, MIMEtype, key)
148 # XXX This code should somehow check for the needsterminal flag.
151 test = subst(e['test'], filename, plist)
152 if test and os.system(test) != 0:
154 command = subst(e[key], MIMEtype, filename, plist)
158 def lookup(caps, MIMEtype, key=None):
161 entries = entries + caps[MIMEtype]
162 MIMEtypes = MIMEtype.split('/')
163 MIMEtype = MIMEtypes[0] + '/*'
165 entries = entries + caps[MIMEtype]
167 entries = filter(lambda e, key=key: key in e, entries)
170 def subst(field, MIMEtype, filename, plist=[]):
171 # XXX Actually, this is Unix-specific
175 c = field[i]; i = i+1
178 c = field[i:i+1]; i = i+1
181 c = field[i]; i = i+1
190 while i < n and field[i] != '}':
192 name = field[start:i]
194 res = res + findparam(name, plist)
196 # %n == number of parts if type is multipart/*
197 # %F == list of alternating type and filename for parts
202 def findparam(name, plist):
203 name = name.lower() + '='
206 if p[:n].lower() == name:
211 # Part 4: test program.
219 for i in range(1, len(sys.argv), 2):
220 args = sys.argv[i:i+2]
222 print "usage: mailcap [MIMEtype file] ..."
226 command, e = findmatch(caps, MIMEtype, 'view', file)
228 print "No viewer found for", type
230 print "Executing:", command
231 sts = os.system(command)
233 print "Exit status:", sts
236 print "Mailcap files:"
237 for fn in listmailcapfiles(): print "\t" + fn
239 if not caps: caps = getcaps()
240 print "Mailcap entries:"
251 print " %-15s" % k, e[k]
254 if __name__ == '__main__':