home *** CD-ROM | disk | FTP | other *** search
/ linuxmafia.com 2016 / linuxmafia.com.tar / linuxmafia.com / pub / palmos / pippy-0.6beta-src.tar.gz / pippy-0.6beta-src.tar / pippy-0.6beta-src / src / Lib / mailcap.py < prev    next >
Text File  |  2000-12-21  |  8KB  |  256 lines

  1. """Mailcap file handling.  See RFC 1524."""
  2.  
  3. import os
  4. import string
  5.  
  6.  
  7. # Part 1: top-level interface.
  8.  
  9. def getcaps():
  10.     """Return a dictionary containing the mailcap database.
  11.     
  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".
  17.  
  18.     """
  19.     caps = {}
  20.     for mailcap in listmailcapfiles():
  21.         try:
  22.             fp = open(mailcap, 'r')
  23.         except:
  24.             continue
  25.         morecaps = readmailcapfile(fp)
  26.         fp.close()
  27.         for key in morecaps.keys():
  28.             if not caps.has_key(key):
  29.                 caps[key] = morecaps[key]
  30.             else:
  31.                 caps[key] = caps[key] + morecaps[key]
  32.     return caps
  33.  
  34. def listmailcapfiles():
  35.     """Return a list of all mailcap files found on the system."""
  36.     # XXX Actually, this is Unix-specific
  37.     if os.environ.has_key('MAILCAPS'):
  38.         str = os.environ['MAILCAPS']
  39.         mailcaps = string.splitfields(str, ':')
  40.     else:
  41.         if os.environ.has_key('HOME'):
  42.             home = os.environ['HOME']
  43.         else:
  44.             # Don't bother with getpwuid()
  45.             home = '.' # Last resort
  46.         mailcaps = [home + '/.mailcap', '/etc/mailcap',
  47.                 '/usr/etc/mailcap', '/usr/local/etc/mailcap']
  48.     return mailcaps
  49.  
  50.  
  51. # Part 2: the parser.
  52.  
  53. def readmailcapfile(fp):
  54.     """Read a mailcap file and return a dictionary keyed by MIME type.
  55.  
  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".
  61.     """
  62.     caps = {}
  63.     while 1:
  64.         line = fp.readline()
  65.         if not line: break
  66.         # Ignore comments and blank lines
  67.         if line[0] == '#' or string.strip(line) == '':
  68.             continue
  69.         nextline = line
  70.         # Join continuation lines
  71.         while nextline[-2:] == '\\\n':
  72.             nextline = fp.readline()
  73.             if not nextline: nextline = '\n'
  74.             line = line[:-2] + nextline
  75.         # Parse the line
  76.         key, fields = parseline(line)
  77.         if not (key and fields):
  78.             continue
  79.         # Normalize the key
  80.         types = string.splitfields(key, '/')
  81.         for j in range(len(types)):
  82.             types[j] = string.strip(types[j])
  83.         key = string.lower(string.joinfields(types, '/'))
  84.         # Update the database
  85.         if caps.has_key(key):
  86.             caps[key].append(fields)
  87.         else:
  88.             caps[key] = [fields]
  89.     return caps
  90.  
  91. def parseline(line):
  92.     """Parse one entry in a mailcap file and return a dictionary.
  93.  
  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.
  96.     """
  97.     fields = []
  98.     i, n = 0, len(line)
  99.     while i < n:
  100.         field, i = parsefield(line, i, n)
  101.         fields.append(field)
  102.         i = i+1 # Skip semicolon
  103.     if len(fields) < 2:
  104.         return None, None
  105.     key, view, rest = fields[0], fields[1], fields[2:]
  106.     fields = {'view': view}
  107.     for field in rest:
  108.         i = string.find(field, '=')
  109.         if i < 0:
  110.             fkey = field
  111.             fvalue = ""
  112.         else:
  113.             fkey = string.strip(field[:i])
  114.             fvalue = string.strip(field[i+1:])
  115.         if fields.has_key(fkey):
  116.             # Ignore it
  117.             pass
  118.         else:
  119.             fields[fkey] = fvalue
  120.     return key, fields
  121.  
  122. def parsefield(line, i, n):
  123.     """Separate one key-value pair in a mailcap entry."""
  124.     start = i
  125.     while i < n:
  126.         c = line[i]
  127.         if c == ';':
  128.             break
  129.         elif c == '\\':
  130.             i = i+2
  131.         else:
  132.             i = i+1
  133.     return string.strip(line[start:i]), i
  134.  
  135.  
  136. # Part 3: using the database.
  137.  
  138. def findmatch(caps, MIMEtype, key='view', filename="/dev/null", plist=[]):
  139.     """Find a match for a mailcap entry.
  140.     
  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
  144.     entry to use.
  145.  
  146.     """
  147.     entries = lookup(caps, MIMEtype, key)
  148.     # XXX This code should somehow check for the needsterminal flag. 
  149.     for e in entries:
  150.         if e.has_key('test'):
  151.             test = subst(e['test'], filename, plist)
  152.             if test and os.system(test) != 0:
  153.                 continue
  154.         command = subst(e[key], MIMEtype, filename, plist)
  155.         return command, e
  156.     return None, None
  157.  
  158. def lookup(caps, MIMEtype, key=None):
  159.     entries = []
  160.     if caps.has_key(MIMEtype):
  161.         entries = entries + caps[MIMEtype]
  162.     MIMEtypes = string.splitfields(MIMEtype, '/')
  163.     MIMEtype = MIMEtypes[0] + '/*'
  164.     if caps.has_key(MIMEtype):
  165.         entries = entries + caps[MIMEtype]
  166.     if key is not None:
  167.         entries = filter(lambda e, key=key: e.has_key(key), entries)
  168.     return entries
  169.  
  170. def subst(field, MIMEtype, filename, plist=[]):
  171.     # XXX Actually, this is Unix-specific
  172.     res = ''
  173.     i, n = 0, len(field)
  174.     while i < n:
  175.         c = field[i]; i = i+1
  176.         if c <> '%':
  177.             if c == '\\':
  178.                 c = field[i:i+1]; i = i+1
  179.             res = res + c
  180.         else:
  181.             c = field[i]; i = i+1
  182.             if c == '%':
  183.                 res = res + c
  184.             elif c == 's':
  185.                 res = res + filename
  186.             elif c == 't':
  187.                 res = res + MIMEtype
  188.             elif c == '{':
  189.                 start = i
  190.                 while i < n and field[i] <> '}':
  191.                     i = i+1
  192.                 name = field[start:i]
  193.                 i = i+1
  194.                 res = res + findparam(name, plist)
  195.             # XXX To do:
  196.             # %n == number of parts if type is multipart/*
  197.             # %F == list of alternating type and filename for parts
  198.             else:
  199.                 res = res + '%' + c
  200.     return res
  201.  
  202. def findparam(name, plist):
  203.     name = string.lower(name) + '='
  204.     n = len(name)
  205.     for p in plist:
  206.         if string.lower(p[:n]) == name:
  207.             return p[n:]
  208.     return ''
  209.  
  210.  
  211. # Part 4: test program.
  212.  
  213. def test():
  214.     import sys
  215.     caps = getcaps()
  216.     if not sys.argv[1:]:
  217.         show(caps)
  218.         return
  219.     for i in range(1, len(sys.argv), 2):
  220.         args = sys.argv[i:i+2]
  221.         if len(args) < 2:
  222.             print "usage: mailcap [MIMEtype file] ..."
  223.             return
  224.         MIMEtype = args[0]
  225.         file = args[1]
  226.         command, e = findmatch(caps, MIMEtype, 'view', file)
  227.         if not command:
  228.             print "No viewer found for", type
  229.         else:
  230.             print "Executing:", command
  231.             sts = os.system(command)
  232.             if sts:
  233.                 print "Exit status:", sts
  234.  
  235. def show(caps):
  236.     print "Mailcap files:"
  237.     for fn in listmailcapfiles(): print "\t" + fn
  238.     print
  239.     if not caps: caps = getcaps()
  240.     print "Mailcap entries:"
  241.     print
  242.     ckeys = caps.keys()
  243.     ckeys.sort()
  244.     for type in ckeys:
  245.         print type
  246.         entries = caps[type]
  247.         for e in entries:
  248.             keys = e.keys()
  249.             keys.sort()
  250.             for k in keys:
  251.                 print "  %-15s" % k, e[k]
  252.             print
  253.  
  254. if __name__ == '__main__':
  255.     test()
  256.