1 """Mailcap file handling. See RFC 1524."""
7 # Part 1: top-level interface.
10 """Return a dictionary containing the mailcap database.
12 The dictionary maps a MIME type (in all lowercase,
13 e.g. 'text/plain') to a list of corresponding mailcap entries.
17 for mailcap
in listmailcapfiles():
19 fp
= open(mailcap
, 'r')
22 morecaps
= readmailcapfile(fp
)
24 for key
in morecaps
.keys():
25 if not caps
.has_key(key
):
26 caps
[key
] = morecaps
[key
]
28 caps
[key
] = caps
[key
] + morecaps
[key
]
31 def listmailcapfiles():
32 """Return a list of all mailcap files found on the system."""
33 # XXX Actually, this is Unix-specific
34 if os
.environ
.has_key('MAILCAPS'):
35 str = os
.environ
['MAILCAPS']
36 mailcaps
= string
.splitfields(str, ':')
38 if os
.environ
.has_key('HOME'):
39 home
= os
.environ
['HOME']
41 # Don't bother with getpwuid()
42 home
= '.' # Last resort
43 mailcaps
= [home
+ '/.mailcap', '/etc/mailcap',
44 '/usr/etc/mailcap', '/usr/local/etc/mailcap']
50 def readmailcapfile(fp
):
55 # Ignore comments and blank lines
56 if line
[0] == '#' or string
.strip(line
) == '':
59 # Join continuation lines
60 while nextline
[-2:] == '\\\n':
61 nextline
= fp
.readline()
62 if not nextline
: nextline
= '\n'
63 line
= line
[:-2] + nextline
65 key
, fields
= parseline(line
)
66 if not (key
and fields
):
69 types
= string
.splitfields(key
, '/')
70 for j
in range(len(types
)):
71 types
[j
] = string
.strip(types
[j
])
72 key
= string
.lower(string
.joinfields(types
, '/'))
75 caps
[key
].append(fields
)
84 field
, i
= parsefield(line
, i
, n
)
86 i
= i
+1 # Skip semicolon
89 key
, view
, rest
= fields
[0], fields
[1], fields
[2:]
90 fields
= {'view': view
}
92 i
= string
.find(field
, '=')
97 fkey
= string
.strip(field
[:i
])
98 fvalue
= string
.strip(field
[i
+1:])
99 if fields
.has_key(fkey
):
103 fields
[fkey
] = fvalue
106 def parsefield(line
, i
, n
):
116 return string
.strip(line
[start
:i
]), i
119 # Part 3: using the database.
121 def findmatch(caps
, MIMEtype
, key
='view', filename
="/dev/null", plist
=[]):
122 """Find a match for a mailcap entry.
124 Return a tuple containing the command line, and the mailcap entry
125 used; (None, None) if no match is found. This may invoke the
126 'test' command of several matching entries before deciding which
130 entries
= lookup(caps
, MIMEtype
, key
)
131 # XXX This code should somehow check for the needsterminal flag.
133 if e
.has_key('test'):
134 test
= subst(e
['test'], filename
, plist
)
135 if test
and os
.system(test
) != 0:
137 command
= subst(e
[key
], MIMEtype
, filename
, plist
)
141 def lookup(caps
, MIMEtype
, key
=None):
143 if caps
.has_key(MIMEtype
):
144 entries
= entries
+ caps
[MIMEtype
]
145 MIMEtypes
= string
.splitfields(MIMEtype
, '/')
146 MIMEtype
= MIMEtypes
[0] + '/*'
147 if caps
.has_key(MIMEtype
):
148 entries
= entries
+ caps
[MIMEtype
]
150 entries
= filter(lambda e
, key
=key
: e
.has_key(key
), entries
)
153 def subst(field
, MIMEtype
, filename
, plist
=[]):
154 # XXX Actually, this is Unix-specific
158 c
= field
[i
]; i
= i
+1
161 c
= field
[i
:i
+1]; i
= i
+1
164 c
= field
[i
]; i
= i
+1
173 while i
< n
and field
[i
] <> '}':
175 name
= field
[start
:i
]
177 res
= res
+ findparam(name
, plist
)
179 # %n == number of parts if type is multipart/*
180 # %F == list of alternating type and filename for parts
185 def findparam(name
, plist
):
186 name
= string
.lower(name
) + '='
189 if string
.lower(p
[:n
]) == name
:
194 # Part 4: test program.
202 for i
in range(1, len(sys
.argv
), 2):
203 args
= sys
.argv
[i
:i
+2]
205 print "usage: mailcap [MIMEtype file] ..."
209 command
, e
= findmatch(caps
, MIMEtype
, 'view', file)
211 print "No viewer found for", type
213 print "Executing:", command
214 sts
= os
.system(command
)
216 print "Exit status:", sts
219 print "Mailcap files:"
220 for fn
in listmailcapfiles(): print "\t" + fn
222 if not caps
: caps
= getcaps()
223 print "Mailcap entries:"
234 print " %-15s" % k
, e
[k
]
237 if __name__
== '__main__':