home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 10 Tools / 10-Tools.zip / pyos2bin.zip / Lib / mailcap.py < prev    next >
Text File  |  1997-03-25  |  6KB  |  239 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,
  13.     e.g. 'text/plain') to a list of corresponding mailcap entries.
  14.  
  15.     """
  16.     caps = {}
  17.     for mailcap in listmailcapfiles():
  18.     try:
  19.         fp = open(mailcap, 'r')
  20.     except:
  21.         continue
  22.     morecaps = readmailcapfile(fp)
  23.     fp.close()
  24.     for key in morecaps.keys():
  25.         if not caps.has_key(key):
  26.         caps[key] = morecaps[key]
  27.         else:
  28.         caps[key] = caps[key] + morecaps[key]
  29.     return caps
  30.  
  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, ':')
  37.     else:
  38.     if os.environ.has_key('HOME'):
  39.         home = os.environ['HOME']
  40.     else:
  41.         # Don't bother with getpwuid()
  42.         home = '.' # Last resort
  43.     mailcaps = [home + '/.mailcap', '/etc/mailcap',
  44.         '/usr/etc/mailcap', '/usr/local/etc/mailcap']
  45.     return mailcaps
  46.  
  47.  
  48. # Part 2: the parser.
  49.  
  50. def readmailcapfile(fp):
  51.     caps = {}
  52.     while 1:
  53.     line = fp.readline()
  54.     if not line: break
  55.     # Ignore comments and blank lines
  56.     if line[0] == '#' or string.strip(line) == '':
  57.         continue
  58.     nextline = 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
  64.     # Parse the line
  65.     key, fields = parseline(line)
  66.     if not (key and fields):
  67.         continue
  68.     # Normalize the key
  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, '/'))
  73.     # Update the database
  74.     if caps.has_key(key):
  75.         caps[key].append(fields)
  76.     else:
  77.         caps[key] = [fields]
  78.     return caps
  79.  
  80. def parseline(line):
  81.     fields = []
  82.     i, n = 0, len(line)
  83.     while i < n:
  84.     field, i = parsefield(line, i, n)
  85.     fields.append(field)
  86.     i = i+1 # Skip semicolon
  87.     if len(fields) < 2:
  88.     return None, None
  89.     key, view, rest = fields[0], fields[1], fields[2:]
  90.     fields = {'view': view}
  91.     for field in rest:
  92.     i = string.find(field, '=')
  93.     if i < 0:
  94.         fkey = field
  95.         fvalue = ""
  96.     else:
  97.         fkey = string.strip(field[:i])
  98.         fvalue = string.strip(field[i+1:])
  99.     if fields.has_key(fkey):
  100.         # Ignore it
  101.         pass
  102.     else:
  103.         fields[fkey] = fvalue
  104.     return key, fields
  105.  
  106. def parsefield(line, i, n):
  107.     start = i
  108.     while i < n:
  109.     c = line[i]
  110.     if c == ';':
  111.         break
  112.     elif c == '\\':
  113.         i = i+2
  114.     else:
  115.         i = i+1
  116.     return string.strip(line[start:i]), i
  117.  
  118.  
  119. # Part 3: using the database.
  120.  
  121. def findmatch(caps, MIMEtype, key='view', filename="/dev/null", plist=[]):
  122.     """Find a match for a mailcap entry.
  123.     
  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
  127.     entry to use.
  128.  
  129.     """
  130.     entries = lookup(caps, MIMEtype, key)
  131.     # XXX This code should somehow check for the needsterminal flag. 
  132.     for e in entries:
  133.     if e.has_key('test'):
  134.         test = subst(e['test'], filename, plist)
  135.         if test and os.system(test) != 0:
  136.         continue
  137.     command = subst(e[key], MIMEtype, filename, plist)
  138.     return command, e
  139.     return None, None
  140.  
  141. def lookup(caps, MIMEtype, key=None):
  142.     entries = []
  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]
  149.     if key is not None:
  150.     entries = filter(lambda e, key=key: e.has_key(key), entries)
  151.     return entries
  152.  
  153. def subst(field, MIMEtype, filename, plist=[]):
  154.     # XXX Actually, this is Unix-specific
  155.     res = ''
  156.     i, n = 0, len(field)
  157.     while i < n:
  158.     c = field[i]; i = i+1
  159.     if c <> '%':
  160.         if c == '\\':
  161.         c = field[i:i+1]; i = i+1
  162.         res = res + c
  163.     else:
  164.         c = field[i]; i = i+1
  165.         if c == '%':
  166.         res = res + c
  167.         elif c == 's':
  168.         res = res + filename
  169.         elif c == 't':
  170.         res = res + MIMEtype
  171.         elif c == '{':
  172.         start = i
  173.         while i < n and field[i] <> '}':
  174.             i = i+1
  175.         name = field[start:i]
  176.         i = i+1
  177.         res = res + findparam(name, plist)
  178.         # XXX To do:
  179.         # %n == number of parts if type is multipart/*
  180.         # %F == list of alternating type and filename for parts
  181.         else:
  182.         res = res + '%' + c
  183.     return res
  184.  
  185. def findparam(name, plist):
  186.     name = string.lower(name) + '='
  187.     n = len(name)
  188.     for p in plist:
  189.     if string.lower(p[:n]) == name:
  190.         return p[n:]
  191.     return ''
  192.  
  193.  
  194. # Part 4: test program.
  195.  
  196. def test():
  197.     import sys
  198.     caps = getcaps()
  199.     if not sys.argv[1:]:
  200.     show(caps)
  201.     return
  202.     for i in range(1, len(sys.argv), 2):
  203.     args = sys.argv[i:i+2]
  204.     if len(args) < 2:
  205.         print "usage: mailcap [MIMEtype file] ..."
  206.         return
  207.     MIMEtype = args[0]
  208.     file = args[1]
  209.     command, e = findmatch(caps, MIMEtype, 'view', file)
  210.     if not command:
  211.         print "No viewer found for", type
  212.     else:
  213.         print "Executing:", command
  214.         sts = os.system(command)
  215.         if sts:
  216.         print "Exit status:", sts
  217.  
  218. def show(caps):
  219.     print "Mailcap files:"
  220.     for fn in listmailcapfiles(): print "\t" + fn
  221.     print
  222.     if not caps: caps = getcaps()
  223.     print "Mailcap entries:"
  224.     print
  225.     ckeys = caps.keys()
  226.     ckeys.sort()
  227.     for type in ckeys:
  228.     print type
  229.     entries = caps[type]
  230.     for e in entries:
  231.         keys = e.keys()
  232.         keys.sort()
  233.         for k in keys:
  234.         print "  %-15s" % k, e[k]
  235.         print
  236.  
  237. if __name__ == '__main__':
  238.     test()
  239.