mirror of
				https://github.com/python/cpython.git
				synced 2025-10-31 13:41:24 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			238 lines
		
	
	
	
		
			5.5 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
			
		
		
	
	
			238 lines
		
	
	
	
		
			5.5 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
| """Mailcap file handling.  See RFC 1524."""
 | |
| 
 | |
| import os
 | |
| import string
 | |
| 
 | |
| 
 | |
| # 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 corresponding mailcap entries.
 | |
| 
 | |
|     """
 | |
|     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():
 | |
|     """Return a list of all mailcap files found on the system."""
 | |
|     # 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):
 | |
| 	    continue
 | |
| 	# 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, 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 e.has_key('test'):
 | |
| 	    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 caps.has_key(MIMEtype):
 | |
| 	entries = entries + caps[MIMEtype]
 | |
|     MIMEtypes = string.splitfields(MIMEtype, '/')
 | |
|     MIMEtype = MIMEtypes[0] + '/*'
 | |
|     if caps.has_key(MIMEtype):
 | |
| 	entries = entries + caps[MIMEtype]
 | |
|     if key is not None:
 | |
| 	entries = filter(lambda e, key=key: e.has_key(key), entries)
 | |
|     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 = 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 [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)
 | |
| 	    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()
 | 
