~inkscape.dev/inkscape-devlibs64/trunk

« back to all changes in this revision

Viewing changes to python/Lib/mailcap.py

  • Committer: Eduard Braun
  • Date: 2016-10-22 16:51:19 UTC
  • Revision ID: eduard.braun2@gmx.de-20161022165119-9eosgy6lp8j1kzli
Update Python to version 2.7.12

Included modules:
  coverage 4.2
  lxml 3.6.4
  numpy 1.11.2
  scour 0.35
  six 1.10.0

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
"""Mailcap file handling.  See RFC 1524."""
2
 
 
3
 
import os
4
 
 
5
 
__all__ = ["getcaps","findmatch"]
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 IOError:
24
 
            continue
25
 
        with fp:
26
 
            morecaps = readmailcapfile(fp)
27
 
        for key, value in morecaps.iteritems():
28
 
            if not key in caps:
29
 
                caps[key] = value
30
 
            else:
31
 
                caps[key] = caps[key] + value
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 'MAILCAPS' in os.environ:
38
 
        str = os.environ['MAILCAPS']
39
 
        mailcaps = str.split(':')
40
 
    else:
41
 
        if 'HOME' in os.environ:
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 line.strip() == '':
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 = key.split('/')
81
 
        for j in range(len(types)):
82
 
            types[j] = types[j].strip()
83
 
        key = '/'.join(types).lower()
84
 
        # Update the database
85
 
        if key in caps:
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 = field.find('=')
109
 
        if i < 0:
110
 
            fkey = field
111
 
            fvalue = ""
112
 
        else:
113
 
            fkey = field[:i].strip()
114
 
            fvalue = field[i+1:].strip()
115
 
        if fkey in fields:
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 line[start:i].strip(), 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 'test' in e:
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 MIMEtype in caps:
161
 
        entries = entries + caps[MIMEtype]
162
 
    MIMEtypes = MIMEtype.split('/')
163
 
    MIMEtype = MIMEtypes[0] + '/*'
164
 
    if MIMEtype in caps:
165
 
        entries = entries + caps[MIMEtype]
166
 
    if key is not None:
167
 
        entries = filter(lambda e, key=key: key in e, 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 = name.lower() + '='
204
 
    n = len(name)
205
 
    for p in plist:
206
 
        if p[:n].lower() == 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()
 
1
"""Mailcap file handling.  See RFC 1524."""
 
2
 
 
3
import os
 
4
 
 
5
__all__ = ["getcaps","findmatch"]
 
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 IOError:
 
24
            continue
 
25
        with fp:
 
26
            morecaps = readmailcapfile(fp)
 
27
        for key, value in morecaps.iteritems():
 
28
            if not key in caps:
 
29
                caps[key] = value
 
30
            else:
 
31
                caps[key] = caps[key] + value
 
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 'MAILCAPS' in os.environ:
 
38
        str = os.environ['MAILCAPS']
 
39
        mailcaps = str.split(':')
 
40
    else:
 
41
        if 'HOME' in os.environ:
 
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 line.strip() == '':
 
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 = key.split('/')
 
81
        for j in range(len(types)):
 
82
            types[j] = types[j].strip()
 
83
        key = '/'.join(types).lower()
 
84
        # Update the database
 
85
        if key in caps:
 
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 = field.find('=')
 
109
        if i < 0:
 
110
            fkey = field
 
111
            fvalue = ""
 
112
        else:
 
113
            fkey = field[:i].strip()
 
114
            fvalue = field[i+1:].strip()
 
115
        if fkey in fields:
 
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 line[start:i].strip(), 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 'test' in e:
 
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 MIMEtype in caps:
 
161
        entries = entries + caps[MIMEtype]
 
162
    MIMEtypes = MIMEtype.split('/')
 
163
    MIMEtype = MIMEtypes[0] + '/*'
 
164
    if MIMEtype in caps:
 
165
        entries = entries + caps[MIMEtype]
 
166
    if key is not None:
 
167
        entries = filter(lambda e, key=key: key in e, 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 = name.lower() + '='
 
204
    n = len(name)
 
205
    for p in plist:
 
206
        if p[:n].lower() == 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()