line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
# Copyrights 1995-2019 by [Mark Overmeer ]. |
2
|
|
|
|
|
|
|
# For other contributors see ChangeLog. |
3
|
|
|
|
|
|
|
# See the manual pages for details on the licensing terms. |
4
|
|
|
|
|
|
|
# Pod stripped from pm file by OODoc 2.02. |
5
|
|
|
|
|
|
|
# This code is part of the bundle MailTools. Meta-POD processed with |
6
|
|
|
|
|
|
|
# OODoc into POD and HTML manual-pages. See README.md for Copyright. |
7
|
|
|
|
|
|
|
# Licensed under the same terms as Perl itself. |
8
|
|
|
|
|
|
|
|
9
|
|
|
|
|
|
|
package Mail::Cap; |
10
|
2
|
|
|
2
|
|
966
|
use vars '$VERSION'; |
|
2
|
|
|
|
|
4
|
|
|
2
|
|
|
|
|
108
|
|
11
|
|
|
|
|
|
|
$VERSION = '2.21'; |
12
|
|
|
|
|
|
|
|
13
|
|
|
|
|
|
|
|
14
|
2
|
|
|
2
|
|
11
|
use strict; |
|
2
|
|
|
|
|
4
|
|
|
2
|
|
|
|
|
4695
|
|
15
|
|
|
|
|
|
|
|
16
|
0
|
|
|
0
|
0
|
0
|
sub Version { our $VERSION } |
17
|
|
|
|
|
|
|
|
18
|
|
|
|
|
|
|
|
19
|
|
|
|
|
|
|
our $useCache = 1; # don't evaluate tests every time |
20
|
|
|
|
|
|
|
|
21
|
|
|
|
|
|
|
my @path; |
22
|
|
|
|
|
|
|
if($^O eq "MacOS") |
23
|
|
|
|
|
|
|
{ @path = split /\,/, $ENV{MAILCAPS} || "$ENV{HOME}mailcap"; |
24
|
|
|
|
|
|
|
} |
25
|
|
|
|
|
|
|
else |
26
|
|
|
|
|
|
|
{ @path = split /\:/ |
27
|
|
|
|
|
|
|
, ( $ENV{MAILCAPS} || (defined $ENV{HOME} ? "$ENV{HOME}/.mailcap:" : '') |
28
|
|
|
|
|
|
|
. '/etc/mailcap:/usr/etc/mailcap:/usr/local/etc/mailcap' |
29
|
|
|
|
|
|
|
); # this path is specified under RFC1524 appendix A |
30
|
|
|
|
|
|
|
} |
31
|
|
|
|
|
|
|
|
32
|
|
|
|
|
|
|
#-------- |
33
|
|
|
|
|
|
|
|
34
|
|
|
|
|
|
|
sub new |
35
|
1
|
|
|
1
|
1
|
233
|
{ my $class = shift; |
36
|
|
|
|
|
|
|
|
37
|
1
|
50
|
|
|
|
8
|
unshift @_, 'filename' if @_ % 2; |
38
|
1
|
|
|
|
|
6
|
my %args = @_; |
39
|
|
|
|
|
|
|
|
40
|
1
|
|
33
|
|
|
6
|
my $take_all = $args{take} && uc $args{take} eq 'ALL'; |
41
|
|
|
|
|
|
|
|
42
|
1
|
|
|
|
|
4
|
my $self = bless {_count => 0}, $class; |
43
|
|
|
|
|
|
|
|
44
|
|
|
|
|
|
|
$self->_process_file($args{filename}) |
45
|
1
|
50
|
33
|
|
|
30
|
if defined $args{filename} && -r $args{filename}; |
46
|
|
|
|
|
|
|
|
47
|
1
|
50
|
33
|
|
|
10
|
if(!defined $args{filename} || $take_all) |
48
|
0
|
|
|
|
|
0
|
{ foreach my $fname (@path) |
49
|
0
|
0
|
|
|
|
0
|
{ -r $fname or next; |
50
|
|
|
|
|
|
|
|
51
|
0
|
|
|
|
|
0
|
$self->_process_file($fname); |
52
|
0
|
0
|
|
|
|
0
|
last unless $take_all; |
53
|
|
|
|
|
|
|
} |
54
|
|
|
|
|
|
|
} |
55
|
|
|
|
|
|
|
|
56
|
1
|
50
|
|
|
|
4
|
unless($self->{_count}) |
57
|
|
|
|
|
|
|
{ # Set up default mailcap |
58
|
0
|
|
|
|
|
0
|
$self->{'audio/*'} = [{'view' => "showaudio %s"}]; |
59
|
0
|
|
|
|
|
0
|
$self->{'image/*'} = [{'view' => "xv %s"}]; |
60
|
0
|
|
|
|
|
0
|
$self->{'message/rfc822'} = [{'view' => "xterm -e metamail %s"}]; |
61
|
|
|
|
|
|
|
} |
62
|
|
|
|
|
|
|
|
63
|
1
|
|
|
|
|
5
|
$self; |
64
|
|
|
|
|
|
|
} |
65
|
|
|
|
|
|
|
|
66
|
|
|
|
|
|
|
sub _process_file |
67
|
1
|
|
|
1
|
|
2
|
{ my $self = shift; |
68
|
1
|
50
|
|
|
|
4
|
my $file = shift or return; |
69
|
|
|
|
|
|
|
|
70
|
1
|
|
|
|
|
5
|
local *MAILCAP; |
71
|
1
|
50
|
|
|
|
37
|
open MAILCAP, $file |
72
|
|
|
|
|
|
|
or return; |
73
|
|
|
|
|
|
|
|
74
|
1
|
|
|
|
|
12
|
$self->{_file} = $file; |
75
|
|
|
|
|
|
|
|
76
|
1
|
|
|
|
|
2
|
local $_; |
77
|
1
|
|
|
|
|
17
|
while() |
78
|
13
|
100
|
|
|
|
35
|
{ next if /^\s*#/; # comment |
79
|
12
|
100
|
|
|
|
67
|
next if /^\s*$/; # blank line |
80
|
5
|
|
|
|
|
64
|
$_ .= # continuation line |
81
|
|
|
|
|
|
|
while s/(^|[^\\])((?:\\\\)*)\\\s*$/$1$2/; |
82
|
5
|
|
|
|
|
12
|
chomp; |
83
|
5
|
|
|
|
|
8
|
s/\0//g; # ensure no NULs in the line |
84
|
5
|
|
|
|
|
35
|
s/(^|[^\\]);/$1\0/g; # make field separator NUL |
85
|
5
|
|
|
|
|
35
|
my ($type, $view, @parts) = split /\s*\0\s*/; |
86
|
|
|
|
|
|
|
|
87
|
5
|
100
|
|
|
|
15
|
$type .= "/*" if $type !~ m[/]; |
88
|
5
|
|
|
|
|
10
|
$view =~ s/\\;/;/g; |
89
|
5
|
|
|
|
|
8
|
$view =~ s/\\\\/\\/g; |
90
|
5
|
|
|
|
|
12
|
my %field = (view => $view); |
91
|
|
|
|
|
|
|
|
92
|
5
|
|
|
|
|
12
|
foreach (@parts) |
93
|
6
|
|
|
|
|
33
|
{ my($key, $val) = split /\s*\=\s*/, $_, 2; |
94
|
6
|
100
|
|
|
|
12
|
if(defined $val) |
95
|
3
|
|
|
|
|
6
|
{ $val =~ s/\\;/;/g; |
96
|
3
|
|
|
|
|
5
|
$val =~ s/\\\\/\\/g; |
97
|
3
|
|
|
|
|
16
|
$field{$key} = $val; |
98
|
|
|
|
|
|
|
} |
99
|
|
|
|
|
|
|
else |
100
|
3
|
|
|
|
|
9
|
{ $field{$key} = 1; |
101
|
|
|
|
|
|
|
} |
102
|
|
|
|
|
|
|
} |
103
|
|
|
|
|
|
|
|
104
|
5
|
100
|
|
|
|
13
|
if(my $test = $field{test}) |
105
|
1
|
50
|
|
|
|
5
|
{ unless ($test =~ /\%/) |
106
|
|
|
|
|
|
|
{ # No parameters in test, can perform it right away |
107
|
0
|
|
|
|
|
0
|
system $test; |
108
|
0
|
0
|
|
|
|
0
|
next if $?; |
109
|
|
|
|
|
|
|
} |
110
|
|
|
|
|
|
|
} |
111
|
|
|
|
|
|
|
|
112
|
|
|
|
|
|
|
# record this entry |
113
|
5
|
100
|
|
|
|
13
|
unless(exists $self->{$type}) |
114
|
4
|
|
|
|
|
10
|
{ $self->{$type} = []; |
115
|
4
|
|
|
|
|
5
|
$self->{_count}++; |
116
|
|
|
|
|
|
|
} |
117
|
5
|
|
|
|
|
7
|
push @{$self->{$type}}, \%field; |
|
5
|
|
|
|
|
23
|
|
118
|
|
|
|
|
|
|
} |
119
|
|
|
|
|
|
|
|
120
|
1
|
|
|
|
|
13
|
close MAILCAP; |
121
|
|
|
|
|
|
|
} |
122
|
|
|
|
|
|
|
|
123
|
|
|
|
|
|
|
#------------------ |
124
|
|
|
|
|
|
|
|
125
|
0
|
|
|
0
|
1
|
0
|
sub view { my $self = shift; $self->_run($self->viewCmd(@_)) } |
|
0
|
|
|
|
|
0
|
|
126
|
0
|
|
|
0
|
1
|
0
|
sub compose { my $self = shift; $self->_run($self->composeCmd(@_)) } |
|
0
|
|
|
|
|
0
|
|
127
|
0
|
|
|
0
|
1
|
0
|
sub edit { my $self = shift; $self->_run($self->editCmd(@_)) } |
|
0
|
|
|
|
|
0
|
|
128
|
0
|
|
|
0
|
1
|
0
|
sub print { my $self = shift; $self->_run($self->printCmd(@_)) } |
|
0
|
|
|
|
|
0
|
|
129
|
|
|
|
|
|
|
|
130
|
|
|
|
|
|
|
sub _run($) |
131
|
0
|
|
|
0
|
|
0
|
{ my ($self, $cmd) = @_; |
132
|
0
|
0
|
|
|
|
0
|
defined $cmd or return 0; |
133
|
|
|
|
|
|
|
|
134
|
0
|
|
|
|
|
0
|
system $cmd; |
135
|
0
|
|
|
|
|
0
|
1; |
136
|
|
|
|
|
|
|
} |
137
|
|
|
|
|
|
|
|
138
|
|
|
|
|
|
|
#------------------ |
139
|
|
|
|
|
|
|
|
140
|
4
|
|
|
4
|
1
|
4402
|
sub viewCmd { shift->_createCommand(view => @_) } |
141
|
0
|
|
|
0
|
1
|
0
|
sub composeCmd { shift->_createCommand(compose => @_) } |
142
|
0
|
|
|
0
|
1
|
0
|
sub editCmd { shift->_createCommand(edit => @_) } |
143
|
1
|
|
|
1
|
1
|
604
|
sub printCmd { shift->_createCommand(print => @_) } |
144
|
|
|
|
|
|
|
|
145
|
|
|
|
|
|
|
sub _createCommand($$$) |
146
|
5
|
|
|
5
|
|
35
|
{ my ($self, $method, $type, $file) = @_; |
147
|
5
|
|
|
|
|
21
|
my $entry = $self->getEntry($type, $file); |
148
|
|
|
|
|
|
|
|
149
|
5
|
50
|
33
|
|
|
92
|
$entry && exists $entry->{$method} |
150
|
|
|
|
|
|
|
or return undef; |
151
|
|
|
|
|
|
|
|
152
|
5
|
|
|
|
|
33
|
$self->expandPercentMacros($entry->{$method}, $type, $file); |
153
|
|
|
|
|
|
|
} |
154
|
|
|
|
|
|
|
|
155
|
|
|
|
|
|
|
sub makeName($$) |
156
|
0
|
|
|
0
|
0
|
0
|
{ my ($self, $type, $basename) = @_; |
157
|
0
|
0
|
|
|
|
0
|
my $template = $self->nametemplate($type) |
158
|
|
|
|
|
|
|
or return $basename; |
159
|
|
|
|
|
|
|
|
160
|
0
|
|
|
|
|
0
|
$template =~ s/%s/$basename/g; |
161
|
0
|
|
|
|
|
0
|
$template; |
162
|
|
|
|
|
|
|
} |
163
|
|
|
|
|
|
|
|
164
|
|
|
|
|
|
|
#------------------ |
165
|
|
|
|
|
|
|
|
166
|
|
|
|
|
|
|
sub field($$) |
167
|
1
|
|
|
1
|
1
|
5
|
{ my($self, $type, $field) = @_; |
168
|
1
|
|
|
|
|
3
|
my $entry = $self->getEntry($type); |
169
|
1
|
|
|
|
|
3
|
$entry->{$field}; |
170
|
|
|
|
|
|
|
} |
171
|
|
|
|
|
|
|
|
172
|
|
|
|
|
|
|
|
173
|
1
|
|
|
1
|
1
|
88
|
sub description { shift->field(shift, 'description'); } |
174
|
0
|
|
|
0
|
1
|
0
|
sub textualnewlines { shift->field(shift, 'textualnewlines'); } |
175
|
0
|
|
|
0
|
1
|
0
|
sub x11_bitmap { shift->field(shift, 'x11-bitmap'); } |
176
|
0
|
|
|
0
|
1
|
0
|
sub nametemplate { shift->field(shift, 'nametemplate'); } |
177
|
|
|
|
|
|
|
|
178
|
|
|
|
|
|
|
sub getEntry |
179
|
6
|
|
|
6
|
0
|
13
|
{ my($self, $origtype, $file) = @_; |
180
|
|
|
|
|
|
|
|
181
|
|
|
|
|
|
|
return $self->{_cache}{$origtype} |
182
|
6
|
100
|
66
|
|
|
43
|
if $useCache && exists $self->{_cache}{$origtype}; |
183
|
|
|
|
|
|
|
|
184
|
5
|
|
|
|
|
31
|
my ($fulltype, @params) = split /\s*;\s*/, $origtype; |
185
|
5
|
|
|
|
|
17
|
my ($type, $subtype) = split m[/], $fulltype, 2; |
186
|
5
|
|
100
|
|
|
20
|
$subtype ||= ''; |
187
|
|
|
|
|
|
|
|
188
|
5
|
|
|
|
|
8
|
my $entry; |
189
|
5
|
|
|
|
|
8
|
foreach (@{$self->{"$type/$subtype"}}, @{$self->{"$type/*"}}) |
|
5
|
|
|
|
|
19
|
|
|
5
|
|
|
|
|
17
|
|
190
|
6
|
100
|
|
|
|
14
|
{ if(exists $_->{'test'}) |
191
|
|
|
|
|
|
|
{ # must run test to see if it applies |
192
|
2
|
|
|
|
|
7
|
my $test = $self->expandPercentMacros($_->{'test'}, |
193
|
|
|
|
|
|
|
$origtype, $file); |
194
|
2
|
|
|
|
|
11165
|
system $test; |
195
|
2
|
100
|
|
|
|
107
|
next if $?; |
196
|
|
|
|
|
|
|
} |
197
|
5
|
|
|
|
|
136
|
$entry = { %$_ }; # make copy |
198
|
5
|
|
|
|
|
23
|
last; |
199
|
|
|
|
|
|
|
} |
200
|
5
|
50
|
|
|
|
46
|
$self->{_cache}{$origtype} = $entry if $useCache; |
201
|
5
|
|
|
|
|
62
|
$entry; |
202
|
|
|
|
|
|
|
} |
203
|
|
|
|
|
|
|
|
204
|
|
|
|
|
|
|
sub expandPercentMacros |
205
|
7
|
|
|
7
|
0
|
28
|
{ my ($self, $text, $type, $file) = @_; |
206
|
7
|
50
|
|
|
|
17
|
defined $type or return $text; |
207
|
7
|
50
|
|
|
|
15
|
defined $file or $file = ""; |
208
|
|
|
|
|
|
|
|
209
|
7
|
|
|
|
|
71
|
my ($fulltype, @params) = split /\s*;\s*/, $type; |
210
|
7
|
|
|
|
|
24
|
($type, my $subtype) = split m[/], $fulltype, 2; |
211
|
|
|
|
|
|
|
|
212
|
7
|
|
|
|
|
14
|
my %params; |
213
|
7
|
|
|
|
|
20
|
foreach (@params) |
214
|
5
|
|
|
|
|
34
|
{ my($key, $val) = split /\s*=\s*/, $_, 2; |
215
|
5
|
|
|
|
|
26
|
$params{$key} = $val; |
216
|
|
|
|
|
|
|
} |
217
|
7
|
|
|
|
|
30
|
$text =~ s/\\%/\0/g; # hide all escaped %'s |
218
|
7
|
|
|
|
|
15
|
$text =~ s/%t/$fulltype/g; # expand %t |
219
|
7
|
|
|
|
|
24
|
$text =~ s/%s/$file/g; # expand %s |
220
|
|
|
|
|
|
|
{ # expand %{field} |
221
|
7
|
|
|
|
|
10
|
local $^W = 0; # avoid warnings when expanding %params |
|
7
|
|
|
|
|
53
|
|
222
|
7
|
|
|
|
|
47
|
$text =~ s/%\{\s*(.*?)\s*\}/$params{$1}/g; |
223
|
|
|
|
|
|
|
} |
224
|
7
|
|
|
|
|
24
|
$text =~ s/\0/%/g; |
225
|
7
|
|
|
|
|
77
|
$text; |
226
|
|
|
|
|
|
|
} |
227
|
|
|
|
|
|
|
|
228
|
|
|
|
|
|
|
# This following procedures can be useful for debugging purposes |
229
|
|
|
|
|
|
|
|
230
|
|
|
|
|
|
|
sub dumpEntry |
231
|
0
|
|
|
0
|
0
|
|
{ my($hash, $prefix) = @_; |
232
|
0
|
0
|
|
|
|
|
defined $prefix or $prefix = ""; |
233
|
|
|
|
|
|
|
print "$prefix$_ = $hash->{$_}\n" |
234
|
0
|
|
|
|
|
|
for sort keys %$hash; |
235
|
|
|
|
|
|
|
} |
236
|
|
|
|
|
|
|
|
237
|
|
|
|
|
|
|
sub dump |
238
|
0
|
|
|
0
|
0
|
|
{ my $self = shift; |
239
|
0
|
|
|
|
|
|
foreach (keys %$self) |
240
|
0
|
0
|
|
|
|
|
{ next if /^_/; |
241
|
0
|
|
|
|
|
|
print "$_\n"; |
242
|
0
|
|
|
|
|
|
foreach (@{$self->{$_}}) |
|
0
|
|
|
|
|
|
|
243
|
0
|
|
|
|
|
|
{ dumpEntry($_, "\t"); |
244
|
0
|
|
|
|
|
|
print "\n"; |
245
|
|
|
|
|
|
|
} |
246
|
|
|
|
|
|
|
} |
247
|
|
|
|
|
|
|
|
248
|
0
|
0
|
|
|
|
|
if(exists $self->{_cache}) |
249
|
0
|
|
|
|
|
|
{ print "Cached types\n"; |
250
|
|
|
|
|
|
|
print "\t$_\n" |
251
|
0
|
|
|
|
|
|
for keys %{$self->{_cache}}; |
|
0
|
|
|
|
|
|
|
252
|
|
|
|
|
|
|
} |
253
|
|
|
|
|
|
|
} |
254
|
|
|
|
|
|
|
|
255
|
|
|
|
|
|
|
1; |