mirror of
				https://github.com/python/cpython.git
				synced 2025-10-31 21:51:50 +00:00 
			
		
		
		
	
		
			
	
	
		
			224 lines
		
	
	
	
		
			4.9 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
		
		
			
		
	
	
			224 lines
		
	
	
	
		
			4.9 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
|   | # 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() |