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__':