/[suikacvs]/messaging/manakai/lib/Message/MIME/Charset.pm
Suika

Contents of /messaging/manakai/lib/Message/MIME/Charset.pm

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1.13 - (hide annotations) (download)
Mon Jul 22 02:48:55 2002 UTC (22 years, 4 months ago) by wakaba
Branch: MAIN
Changes since 1.12: +47 -8 lines
2002-07-22  Wakaba <w@suika.fam.cx>

	* Charset.pm:
	- (encode, decode): Reformed.  Return a hash as second or
	greter'th element of returned array.  (Now only 'success'
	key is used.)
	* EncodedWord.pm (_decode_eword): Fix for new 
	Message::MIME::Charset::decode.

1 wakaba 1.1
2     =head1 NAME
3    
4     Message::MIME::Charset Perl module
5    
6     =head1 DESCRIPTION
7    
8     Perl module for MIME charset.
9    
10     =cut
11    
12 wakaba 1.10 ## NOTE: You should not require/use other module (even it
13     ## is part of Message::* Perl Modules) as far as possible,
14     ## to be able to use this module (M::M::Charset) from
15     ## other (non-Message::*) modules.
16    
17 wakaba 1.1 package Message::MIME::Charset;
18     use strict;
19 wakaba 1.12 use vars qw(%CHARSET %MSNAME2IANANAME %REG $VERSION);
20 wakaba 1.13 $VERSION=do{my @r=(q$Revision: 1.12 $=~/\d+/g);sprintf "%d."."%02d" x $#r,@r};
21 wakaba 1.1
22 wakaba 1.9 &_builtin_charset;
23     sub _builtin_charset () {
24 wakaba 1.4
25     $CHARSET{'*DEFAULT'} = {
26     preferred_name => '',
27    
28     encoder => sub { $_[1] },
29     decoder => sub { $_[1] },
30    
31     mime_text => 1, ## Suitability in use as MIME text/* charset
32     #accept_cte => [qw/7bit .../],
33     cte_7bit_preferred => 'quoted-printable',
34     };
35     $CHARSET{'*default'} = $CHARSET{'*DEFAULT'};
36    
37     $CHARSET{'us-ascii'} = {
38     preferred_name => 'us-ascii',
39    
40     encoder => sub { $_[1] },
41     decoder => sub { $_[1] },
42 wakaba 1.5
43     mime_text => 1,
44 wakaba 1.13 cte_7bit_preferred => 'quoted-printable',
45 wakaba 1.4 };
46    
47     $CHARSET{'iso-2022-int-1'} = {
48     preferred_name => 'iso-2022-int-1',
49    
50     encoder => sub { $_[1] },
51     decoder => sub { $_[1] },
52 wakaba 1.5
53     mime_text => 1,
54 wakaba 1.4 };
55    
56     $CHARSET{'unknown-8bit'} = {
57     preferred_name => 'unknown-8bit',
58    
59     encoder => sub { $_[1] },
60     decoder => sub { $_[1] },
61    
62 wakaba 1.13 mime_text => 1,
63     cte_7bit_preferred => 'base64',
64     };
65    
66     $CHARSET{'x-unknown'} = {
67     preferred_name => 'x-unknown',
68    
69     encoder => sub { $_[1] },
70     decoder => sub { $_[1] },
71    
72     mime_text => 0,
73     cte_7bit_preferred => 'base64',
74     };
75    
76     $CHARSET{'*undef'} = {
77     preferred_name => '',
78    
79     #encoder => sub { $_[1] },
80     #decoder => sub { $_[1] },
81    
82 wakaba 1.4 mime_text => 0,
83 wakaba 1.6 cte_7bit_preferred => 'base64',
84 wakaba 1.4 };
85    
86 wakaba 1.9 } # /builtin_charset
87    
88     my %_MINIMUMIZER = (
89     'euc-jp' => \&_name_euc_japan,
90     'euc-jisx0213' => \&_name_euc_japan,
91     'euc-jisx0213-plane1' => \&_name_euc_japan,
92     'x-euc-jisx0213-packed' => \&_name_euc_japan,
93 wakaba 1.11 'x-iso-2022' => \&_name_8bit_iso2022,
94     'iso-2022-cn' => \&_name_8bit_iso2022,
95     'iso-2022-cn-ext' => \&_name_8bit_iso2022,
96 wakaba 1.9 'iso-2022-int-1' => \&_name_net_ascii_8bit,
97 wakaba 1.11 'iso-2022-jp' => \&_name_8bit_iso2022,
98     'iso-2022-jp-1' => \&_name_8bit_iso2022,
99     'iso-2022-jp-2' => \&_name_8bit_iso2022,
100     'iso-2022-jp-3' => \&_name_8bit_iso2022,
101     'iso-2022-jp-3-plane1' => \&_name_8bit_iso2022,
102     'iso-2022-kr' => \&_name_8bit_iso2022,
103     'iso-8859-1' => \&_name_8bit_iso2022,
104 wakaba 1.9 'iso-10646-j-1' => \&_name_utf16be,
105     'iso-10646-ucs-2' => \&_name_utf16be,
106     'iso-10646-ucs-4' => \&_name_utf32be,
107     'iso-10646-ucs-basic' => \&_name_utf16be,
108     'iso-10646-unicode-latin1' => \&_name_utf16be,
109     jis_x0201 => \&_name_shift_jis,
110 wakaba 1.11 junet => \&_name_8bit_iso2022,
111 wakaba 1.9 'x-junet8' => \&_name_net_ascii_8bit,
112     shift_jis => \&_name_shift_jis,
113     shift_jisx0213 => \&_name_shift_jis,
114     'shift_jisx0213-plane1' => \&_name_shift_jis,
115     'x-sjis' => \&_name_shift_jis,
116     'us-ascii' => \&_name_net_ascii_8bit,
117     'utf-8' => \&_name_net_ascii_8bit,
118     'utf-16be' => \&_name_utf16be,
119     'utf-32be' => \&_name_utf32be,
120     );
121    
122 wakaba 1.12 %MSNAME2IANANAME = (
123     'iso-2022-jp' => 'x-iso2022jp-cp932',
124     'ks_c_5601-1987' => 'windows-949',
125     );
126    
127 wakaba 1.4 sub make_charset ($%) {
128     my $name = shift;
129     return unless $name; ## Note: charset "0" is not supported.
130     my %definition = @_;
131 wakaba 1.6
132     $definition{preferred_name} ||= $name;
133 wakaba 1.4 if ($definition{preferred_name} ne $name
134     && ref $CHARSET{$definition{preferred_name}}) {
135     ## New charset is an alias of defined charset,
136     $CHARSET{$name} = $CHARSET{$definition{preferred_name}};
137     return;
138     } elsif ($definition{alias_of} && ref $CHARSET{$definition{alias_of}}) {
139     ## New charset is an alias of defined charset,
140     $CHARSET{$name} = $CHARSET{$definition{alias_of}};
141     return;
142     }
143     $CHARSET{$name} = \%definition;
144    
145     ## Set default values
146 wakaba 1.6 #$definition{encoder} ||= sub { $_[1] };
147     #$definition{decoder} ||= sub { $_[1] };
148 wakaba 1.4
149     $definition{mime_text} = 0 unless defined $definition{mime_text};
150     $definition{cte_7bit_preferred} = 'base64'
151     unless defined $definition{cte_7bit_preferred};
152     }
153 wakaba 1.1
154     sub encode ($$) {
155     my ($charset, $s) = (lc shift, shift);
156 wakaba 1.13 my $c = ref $CHARSET{$charset}->{encoder}? $charset: '*undef';
157     if (ref $CHARSET{$c}->{encoder}) {
158     my ($t, %r) = &{$CHARSET{$c}->{encoder}} ($charset, $s);
159     unless (defined $r{success}) {
160     $r{success} = 1;
161     }
162     return ($t, %r);
163 wakaba 1.1 }
164 wakaba 1.5 ($s, success => 0);
165 wakaba 1.1 }
166    
167     sub decode ($$) {
168     my ($charset, $s) = (lc shift, shift);
169 wakaba 1.13 my $c = ref $CHARSET{$charset}->{decoder}? $charset: '*undef';
170     if (ref $CHARSET{$c}->{decoder}) {
171     my ($t, %r) = &{$CHARSET{$c}->{decoder}} ($charset, $s);
172     unless (defined $r{success}) {
173     $r{success} = 1;
174     }
175     return ($t, %r);
176 wakaba 1.1 }
177 wakaba 1.13 ($s, success => 0);
178 wakaba 1.1 }
179    
180     sub name_normalize ($) {
181     my $name = lc shift;
182 wakaba 1.4 $CHARSET{$name}->{preferred_name} || $name;
183 wakaba 1.1 }
184    
185 wakaba 1.6 sub name_minimumize ($$) {
186     my ($charset, $s) = (lc shift, shift);
187 wakaba 1.9 if (ref $CHARSET{$charset}->{name_minimumizer} eq 'CODE') {
188 wakaba 1.6 return &{$CHARSET{$charset}->{name_minimumizer}} ($charset, $s);
189 wakaba 1.9 } elsif (ref $_MINIMUMIZER{$charset}) {
190     return &{$_MINIMUMIZER{$charset}} ($charset, $s);
191 wakaba 1.13 } elsif (ref $CHARSET{'*undef'}->{name_minimumizer} eq 'CODE') {
192     return &{$CHARSET{'*undef'}->{name_minimumizer}} ($charset, $s);
193 wakaba 1.9 }
194     (charset => $charset);
195     }
196    
197 wakaba 1.12 sub msname2iananame ($) {
198     my $mscharset = shift;
199     $MSNAME2IANANAME{$mscharset} || $mscharset;
200     }
201    
202 wakaba 1.9 sub _name_7bit_iso2022 ($$) {shift;
203     my $s = shift;
204     if ($s =~ /[\x0E\x0F\x1B]/) {
205     return (charset => 'iso-2022-jp')
206     unless $s =~ /\x1B[^\x24\x28]
207     |\x1B\x24[^\x40B]
208     |\x1B\x28[^BJ]
209     |\x0E|\x0F/x;
210     return (charset => 'iso-2022-jp-1')
211     unless $s =~ /\x1B[^\x24\x28]
212     |\x1B\x24[^\x40B\x28]
213     |\x1B\x24\x28[^D]
214     |\x1B\x28[^BJ]
215     |\x0E|\x0F/x;
216     return (charset => 'iso-2022-jp-3-plane1')
217     unless $s =~ /\x1B[^\x24\x28]
218     |\x1B\x24[^\x28] #[^B\x28]
219     |\x1B\x24\x28[^O]
220     |\x1B\x28[^B]
221     |\x0E|\x0F/x;
222     return (charset => 'iso-2022-jp-3')
223     unless $s =~ /\x1B[^\x24\x28]
224     |\x1B\x24[^\x28] #[^B\x28]
225     |\x1B\x24\x28[^OP]
226     |\x1B\x28[^B]
227     |\x0E|\x0F/x;
228     return (charset => 'iso-2022-kr')
229     unless $s =~ /\x1B[^\x24]
230     |\x1B\x24[^\x29]
231     |\x1B\x24\x29[^C]/x;
232     return (charset => 'iso-2022-jp-2')
233     unless $s =~ /\x1B[^\x24\x28\x2E\x4E]
234     |\x1B\x24[^\x40AB\x28]
235     |\x1B\x24\x28[^CD]
236     |\x1B\x28[^BJ]
237     |\x1B\x2E[^AF]
238     |\x0E|\x0F/x;
239     return (charset => 'iso-2022-cn')
240     unless $s =~ /\x1B[^\x4E\x24]
241     |\x1B\x24[^\x29\x2A]
242     |\x1B\x24\x29[^AG]
243     |\x1B\x24\x2A[^H]/x;
244     return (charset => 'iso-2022-cn-ext')
245     unless $s =~ /\x1B[^\x4E\x4F\x24]
246     |\x1B\x24[^\x29\x2A]
247     |\x1B\x24\x29[^AEG]
248     |\x1B\x24\x2A[^HIJKLM]/x;
249     return (charset => 'iso-2022-int-1')
250     unless $s =~ /\x1B[^\x24\x28\x2D]
251     |\x1B\x24[^\x40AB\x28\x29]
252     |\x1B\x24\x28[^DGH]
253     |\x1B\x24\x29[^C]
254     |\x1B\x28[^BJ]
255     |\x1B\x2D[^AF]/x;
256     return (charset => 'junet')
257     unless $s =~ /\x1B[^\x24\x28\x2C]
258     |\x1B\x24[^\x28\x2C\x40-\x42]
259     |\x1B\x24[\x28\x2C][^\x20-\x7E]
260     |\x1B\x24[\x28\x2C][\x20-\x2F]+[^\x30-\x7E]
261     |\x1B[\x28\x2C][^\x20-\x7E]
262     |\x1B[\x28\x2C][\x20-\x2F]+[^\x30-\x7E]
263     |\x0E|\x0F/x;
264     return (charset => 'x-iso-2022');
265     } else {
266     return (charset => 'us-ascii');
267 wakaba 1.6 }
268     }
269    
270 wakaba 1.9 sub _name_net_ascii_8bit ($) {
271     my $name = shift; my $s = shift;
272 wakaba 1.8 return (charset => 'us-ascii') unless $s =~ /[\x1B\x0E\x0F\x80-\xFF]/;
273     if ($s =~ /[\x80-\xFF]/) {
274     if ($s =~ /[\xC0-\xFD][\x80-\xBF]*[\x80-\x8F]/) {
275     if ($s =~ /\x1B/) {
276 wakaba 1.9 return (charset => 'x-junet8'); ## junet + UTF-8
277 wakaba 1.8 } else {
278     return (charset => 'utf-8');
279     }
280     } elsif ($s =~ /\x1B/) {
281 wakaba 1.9 return (charset => 'x-iso-2022'); ## 8bit ISO 2022
282     } else {
283     return (charset => 'iso-8859-1');
284     }
285     } else { ## 7bit ISO 2022
286     return _name_7bit_iso2022 ($name, $s);
287     }
288     }
289    
290 wakaba 1.11 sub _name_8bit_iso2022 ($$) {
291 wakaba 1.9 my $name = shift; my $s = shift;
292     return (charset => 'us-ascii') unless $s =~ /[\x1B\x0E\x0F\x80-\xFF]/;
293     if ($s =~ /[\x80-\xFF]/) {
294     if ($s =~ /\x1B/) {
295     return (charset => 'x-iso-2022'); ## 8bit ISO 2022
296 wakaba 1.8 } else {
297     return (charset => 'iso-8859-1');
298     }
299 wakaba 1.9 } else { ## 7bit ISO 2022
300     return _name_7bit_iso2022 ($name, $s);
301     }
302     }
303    
304     ## Not completed.
305     ## TODO: gb18030, cn-gb-12345
306     ## TODO: _name_euc_gbf (cn-gb-12345, gb2312)
307     sub _name_euc_gb ($$) {
308     my $name = shift; my $s = shift;
309     if ($s =~ /[\x80-\xFF]/) {
310     if ($s =~ /
311     (?:\G|[\x00-\x3F\x7F\x80\xFF])
312     (?:[\xA1-\xA9\xB0-\xFE][\xA1-\xFE]
313     |[\x40-\x7E])*
314     (?:
315     [\x81-\xA0\xAA-\xAF][\x40-\xFE]
316     |[\xA1-\xFE][\x40-\xA0]
317     )
318     /x) {
319     (charset => 'gbk');
320     } elsif ($s =~ /
321     (?:\xA2[\xA1-\xAA]
322     |\xA6[\xE0-\xF5]
323     |\xA8[\xBB-\xC0]
324     )
325     (?=(?:[\xA1-\xFE][\xA1-\xFE])*(?:[\x00-\xA0\xFF]|\z))
326     /x) {
327     (charset => 'gbk');
328     } elsif ($s =~ /
329     (?:\xA3\xE7|\xA7[\xDD-\xF2]
330     |\xA8[\xBB-\xC0]
331     |[\xAA-\xAF\xF8-\xFE][\xA1-\xFE]
332     )
333     (?=(?:[\xA1-\xFE][\xA1-\xFE])*(?:[\x00-\xA0\xFF]|\z))
334     /x) {
335     (charset => 'cn-gb-isoir165', 'charset-edition' => 1992);
336     } elsif ($s =~ /\xEF\xF1 ## Typo bug of GB 2312
337     (?=(?:[\xA1-\xFE][\xA1-\xFE])*(?:[\x00-\xA0\xFF]|\z))
338     /x) {
339     (charset => 'gb2312');
340     } else {
341     (charset => 'gb2312', 'charset-edition' => 1980);
342     }
343     } elsif ($s =~ /[\x0E\x0F]/) {
344     (charset => 'gb2312'); ## Actually, this is not "gb2312"
345     } else {
346 wakaba 1.11 _name_7bit_iso2022 ($name, $s);
347 wakaba 1.9 }
348     }
349    
350     sub _name_euc_japan ($$) {
351     my $name = shift; my $s = shift;
352     if ($s =~ /[\x80-\xFF]/) {
353     if ($s =~ /\x8F[\xA1\xA3-\xA5\xA8\xAC-\xAF\xEE-\xFE][\xA1-\xFE]/) {
354     if ($s =~ /\x8F[\xA2\xA6\xA7\xA9-\xAB\xB0-\xED][\xA1-\xFE]/) {
355     ## JIS X 0213 plane 2 + JIS X 0212
356     (charset => 'x-euc-jisx0213-packed');
357     } else {
358     (charset => 'euc-jisx0213');
359     }
360     } elsif ($s =~ m{(?<![\x8E\x8F]) ## Not G2/G3 character
361     (?: ## JIS X 0213:2000
362     [\xA9-\xAF\xF5-\xFE][\xA1-\xFE]
363     |\xA2[\xAF-\xB9\xC2-\xC9\xD1-\xDB\xE9-\xF1\xFA-\xFD]
364     |\xA3[\xA1-\xAF\xBA-\xC0\xDB-\xE0\xFB-\xFE]
365     |\xA4[\xF4-\xFE]|\xA5[\xF7-\xFE]
366     |\xA6[\xB9-\xC0\xD9-\xFE]|\xA7[\xC2-\xD0\xF2-\xFE]
367     |\xA8[\xC1-\xFE]|\xCF[\xD4-\xFE]|\xF4[\xA7-\xFE]
368     )
369     (?=(?:[\xA1-\xFE][\xA1-\xFE])*(?:[\x00-\xA0\xFF]|\z))}x) {
370     if ($s =~ /\x8F/) { ## JIS X 0213 plane 1 + JIS X 0212
371     (charset => 'x-euc-jisx0213-packed');
372     } else {
373     (charset => 'euc-jisx0213-plane1');
374     }
375     } else {
376     (charset => 'euc-jp');
377     }
378     } elsif ($s =~ /\x0E|\x0F|\x1B[\x4E\x4F]/) {
379     (charset => 'euc-jisx0213'); ## Actually, this is not euc-japan
380     } else {
381 wakaba 1.11 _name_7bit_iso2022 ($name, $s);
382 wakaba 1.9 }
383     }
384    
385     sub _name_shift_jis ($$) {
386     my $name = shift; my $s = shift;
387     if ($s =~ /[\x80-\xFF]/) {
388     if ($s =~ /[\x0E\x0F\x1B]/) {
389     (charset => 'x-sjis');
390     } elsif ($s =~ /
391     (?:\G|[\x00-\x3F\x7F])
392     (?:[\x81-\x9F\xE0-\xFC][\x40-\x7E\x80-\xFC]
393     |[\x40-\x7E\xA1-\xDF])*
394     [\xF0-\xFC][\x40-\x7E\x80-\xFC]
395     /x) {
396     (charset => 'shift_jisx0213');
397     } elsif ($s =~ /
398     (?:\G|[\x00-\x3F\x7F])
399     (?:[\x81-\x9F\xE0-\xFC][\x40-\x7E\x80-\xFC]
400     |[\x40-\x7E\xA1-\xDF])*
401     (?:
402     [\x85-\x87\xEB-\xEF][\x40-\x7E\x80-\xFC]
403     |\x81[\xAD-\xB7\xC0-\xC7\xCF-\xD9\xE9-\xEF\xF8-\xFB]
404     |\x82[\x40-\x4E\x59-\x5F\x7A-\x80\x9B-\x9E\xF2-\xFC]
405     |\x83[\x97-\x9E\xB7-\xBE\xD7-\xFC]
406     |\x84[\x61-\x6F\x72-\x9E\xBF-\xFC]
407     |\x88[\x40-\x9E]|\x98[\x73-\x9E]|\xEA[\xA5-\xFC]
408     )
409     /x) {
410     (charset => 'shift_jisx0213-plane1');
411     } else {
412     (charset => 'shift_jis');
413     }
414     } elsif ($s =~ /[\x5C\x7E]/) {
415     if ($s =~ /\x1B\x0E\x0F/) {
416     (charset => 'x-sjis'); ## ISO 2022 with implied "ESC ( J"
417     ## BUG: "ESC ( B foobar\aaa ESC ( J aiueo" also matchs this
418     } else {
419     (charset => 'jis_x0201');
420     }
421     } else {
422 wakaba 1.11 _name_7bit_iso2022 ($name, $s);
423 wakaba 1.9 }
424     }
425    
426     sub _name_utf16be ($$) {
427     shift; my $s = shift;
428     if ($s =~ /[\xD8-\xDB][\x00-\xFF][\xDC-\xDF][\x00-\xFF]
429     (?=(?:[\x00-\xFF][\x00-\xFF])*\z)/sx) {
430     (charset => 'utf-16be');
431     } elsif ($s =~ /[\x01-\xFF][\x00-\xFF]
432     (?=(?:[\x00-\xFF][\x00-\xFF])*\z)/sx) {
433     if ($s =~ /([^\x00\x03\x04\x23\x25\x30\xFE\xFF]
434     [\x00-\xFF] # ^\x20\x22\x4E-\x9F\xF9\xFA
435     |\x03[^\x00-\x6F\xD0-\xFF]
436     #|\x20[^\x00-\x6F]
437     |\x25[^\x00-\x7F]
438     |\xFE[^\x30-\x4F]
439     |\xFF[^\x00-\xEF]
440     ## note 1 of RFC 1816 is ambitious, so block entire
441     ## is excepted
442     |\x30[\x00-\x3F]
443     )
444     (?=(?:[\x00-\xFF][\x00-\xFF])*\z)/sx) {
445     (charset => 'iso-10646-ucs-2');
446     } else {
447     (charset => 'iso-10646-j-1');
448     }
449     } elsif ($s =~ /\x00[\x80-\xFF]
450     (?=(?:[\x00-\xFF][\x00-\xFF])*\z)/sx) {
451     (charset => 'iso-10646-unicode-latin1');
452     } else {
453     (charset => 'iso-10646-ucs-basic');
454     }
455     }
456    
457     sub _name_utf32be ($$) {
458     shift; my $s = shift;
459     if ($s =~ /
460     ([\x01-\x7F][\x00-\xFF]{3}
461     |\x00[\x11-\xFF][\x00-\xFF][\x00-\xFF])
462     (?=(?:[\x00-\xFF]{4})*\z)/sx) {
463     (charset => 'iso-10646-ucs-4');
464     } else {
465     (charset => 'utf-32be');
466 wakaba 1.8 }
467     }
468    
469 wakaba 1.13 sub _utf8_on ($) {
470     Encode::_utf8_on ($_[0]) if $Encode::VERSION;
471     }
472     sub _utf8_off ($) {
473     Encode::_utf8_off ($_[0]) if $Encode::VERSION;
474     }
475    
476 wakaba 1.1 =head1 LICENSE
477    
478     Copyright 2002 wakaba E<lt>w@suika.fam.cxE<gt>.
479    
480     This program is free software; you can redistribute it and/or modify
481     it under the terms of the GNU General Public License as published by
482     the Free Software Foundation; either version 2 of the License, or
483     (at your option) any later version.
484    
485     This program is distributed in the hope that it will be useful,
486     but WITHOUT ANY WARRANTY; without even the implied warranty of
487     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
488     GNU General Public License for more details.
489    
490     You should have received a copy of the GNU General Public License
491     along with this program; see the file COPYING. If not, write to
492     the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
493     Boston, MA 02111-1307, USA.
494    
495     =head1 CHANGE
496    
497     See F<ChangeLog>.
498 wakaba 1.13 $Date: 2002/07/21 03:25:23 $
499 wakaba 1.1
500     =cut
501    
502     1;

admin@suikawiki.org
ViewVC Help
Powered by ViewVC 1.1.24