/[suikacvs]/messaging/manakai/lib/Message/Field/Params.pm
Suika

Contents of /messaging/manakai/lib/Message/Field/Params.pm

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1.4 - (hide annotations) (download)
Tue Mar 26 05:31:55 2002 UTC (22 years, 8 months ago) by wakaba
Branch: MAIN
Changes since 1.3: +24 -13 lines
2002-03-26  wakaba <w@suika.fam.cx>

	* UA.pm: New module.

1 wakaba 1.1
2     =head1 NAME
3    
4     Message::Field::Params Perl module
5    
6     =head1 DESCRIPTION
7    
8     Perl module for parameters field body (such as C<Content-Type:>).
9    
10     =cut
11    
12     package Message::Field::Params;
13     use strict;
14     require 5.6.0;
15     use re 'eval';
16     use vars qw(%DEFAULT %REG $VERSION);
17 wakaba 1.4 $VERSION=do{my @r=(q$Revision: 1.3 $=~/\d+/g);sprintf "%d."."%02d" x $#r,@r};
18 wakaba 1.3 require Message::Util;
19 wakaba 1.1 use Carp;
20     use overload '@{}' => sub {shift->_delete_empty()->{param}},
21     '""' => sub {shift->stringify};
22    
23     $REG{WSP} = qr/[\x09\x20]/;
24     $REG{FWS} = qr/[\x09\x20]*/;
25    
26     $REG{comment} = qr/\x28(?:\x5C[\x00-\xFF]|[\x00-\x0C\x0E-\x27\x2A-\x5B\x5D-\xFF]+|(??{$REG{comment}}))*\x29/;
27     $REG{quoted_string} = qr/\x22(?:\x5C[\x00-\xFF]|[\x00-\x0C\x0E-\x21\x23-\x5B\x5D-\xFF])*\x22/;
28     $REG{domain_literal} = qr/\x5B(?:\x5C[\x00-\xFF]|[\x00-\x0C\x0E-\x5A\x5E-\xFF])*\x5D/;
29     $REG{atext} = qr/[\x21\x23-\x27\x2A\x2B\x2D\x2F-\x39\x3D\x3F\x41-\x5A\x5E-\x7E]+/;
30     $REG{atext_dot} = qr/[\x21\x23-\x27\x2A\x2B\x2D-\x39\x3D\x3F\x41-\x5A\x5E-\x7E]+/;
31     $REG{token} = qr/[\x21\x23-\x27\x2A\x2B\x2D\x2E\x30-\x39\x41-\x5A\x5E-\x7E]+/;
32 wakaba 1.4 $REG{http_token} = qr/[\x21\x23-\x27\x2A\x2B\x2D\x2E\x30-\x39\x41-\x5A\x5E-\x7A\x7C\x7E]+/;
33 wakaba 1.1 $REG{attribute_char} = qr/[\x21\x23-\x24\x26\x2B\x2D\x2E\x30-\x39\x41-\x5A\x5E-\x7E]+/;
34 wakaba 1.3 $REG{S_encoded_word} = qr/=\x3F$REG{atext_dot}\x3F=/;
35 wakaba 1.1
36     $REG{param} = qr/(?:$REG{atext_dot}|$REG{quoted_string})(?:$REG{atext_dot}|$REG{quoted_string}|$REG{WSP}|,)*/;
37     ## more naive C<parameter>. (Comma is allowed for RFC 1049)
38     $REG{parameter} = qr/$REG{token}=(?:$REG{token}|$REG{quoted_string})?/;
39     ## as defined by RFC 2045, not RFC 2231.
40    
41     $REG{M_quoted_string} = qr/\x22((?:\x5C[\x00-\xFF]|[\x00-\x0C\x0E-\x21\x23-\x5B\x5D-\xFF])*)\x22/;
42     $REG{M_parameter} = qr/($REG{token})=($REG{token}|$REG{quoted_string})?/;
43     ## as defined by RFC 2045, not RFC 2231.
44     $REG{M_parameter_name} = qr/($REG{attribute_char}+)(?:\*([0-9]+)(\*)?|(\*))/;
45     ## as defined by RFC 2231.
46     $REG{M_parameter_extended_value} = qr/([^']*)'([^']*)'($REG{token}*)/;
47     ## as defined by RFC 2231, but more naive.
48    
49     $REG{NON_atext} = qr/[^\x21\x23-\x27\x2A\x2B\x2D\x2F\x30-\x39\x3D\x3F\x41-\x5A\x5E-\x7E]/;
50     $REG{NON_atext_dot} = qr/[^\x21\x23-\x27\x2A\x2B\x2D-\x39\x3D\x3F\x41-\x5A\x5E-\x7E]/;
51 wakaba 1.3 $REG{NON_atext_dot_wsp} = qr/[^\x09\x20\x21\x23-\x27\x2A\x2B\x2D-\x39\x3D\x3F\x41-\x5A\x5E-\x7E]/;
52 wakaba 1.1 $REG{NON_token} = qr/[^\x21\x23-\x27\x2A\x2B\x2D\x2E\x30-\x39\x41-\x5A\x5E-\x7E]/;
53 wakaba 1.3 $REG{NON_token_wsp} = qr/[^\x09\x20\x21\x23-\x27\x2A\x2B\x2D\x2E\x30-\x39\x41-\x5A\x5E-\x7E]/;
54 wakaba 1.4 $REG{NON_http_token} = qr/[^\x21\x23-\x27\x2A\x2B\x2D\x2E\x30-\x39\x41-\x5A\x5E-\x7A\x7C\x7E]/;
55     $REG{NON_http_token_wsp} = qr/[^\x09\x20\x21\x23-\x27\x2A\x2B\x2D\x2E\x30-\x39\x41-\x5A\x5E-\x7A\x7C\x7E]/;
56 wakaba 1.1 $REG{NON_attribute_char} = qr/[^\x21\x23-\x24\x26\x2B\x2D\x2E\x30-\x39\x41-\x5A\x5E-\x7E]/;
57 wakaba 1.4 $REG{NON_http_attribute_char} = qr/[^\x21\x23-\x24\x26\x2B\x2D\x2E\x30-\x39\x41-\x5A\x5E-\x7A\x7C\x7E]/;
58     ## Yes, C<attribute-char> does not appear in HTTP spec.
59 wakaba 1.1
60    
61     %DEFAULT = (
62 wakaba 1.2 delete_fws => 1,
63 wakaba 1.3 encoding_after_encode => '*default',
64     encoding_before_decode => '*default',
65     hook_encode_string => #sub {shift; (value => shift, @_)},
66     \&Message::Util::encode_header_string,
67     hook_decode_string => #sub {shift; (value => shift, @_)},
68     \&Message::Util::decode_header_string,
69 wakaba 1.1 parameter_value_max => 78,
70     use_parameter_extension => -1,
71     );
72    
73     =head2 Message::Field::Params->new ([%option])
74    
75     Returns new Message::Field::Params. Some options can be given as hash.
76    
77     =cut
78    
79     sub new ($;%) {
80     my $class = shift;
81     my $self = bless {option => {@_}}, $class;
82     $self->_initialize_new ();
83     for (keys %DEFAULT) {$self->{option}->{$_} ||= $DEFAULT{$_}}
84     $self;
85     }
86    
87     ## Initialization for new () method.
88     sub _initialize_new ($;%) {
89     my $self = shift;
90     #for (keys %DEFAULT) {$self->{option}->{$_} ||= $DEFAULT{$_}}
91     }
92    
93     =head2 Message::Field::Params->parse ($nantara, [%option])
94    
95     Parse Message::Field::Params and new ContentType instance.
96     Some options can be given as hash.
97    
98     =cut
99    
100     sub parse ($$;%) {
101     my $class = shift;
102     my $body = shift;
103     my $self = bless {option => {@_}}, $class;
104     $self->_initialize_parse ();
105     for (keys %DEFAULT) {$self->{option}->{$_} ||= $DEFAULT{$_}}
106 wakaba 1.2 $body = $self->_delete_comment ($body);
107     $body = $self->_delete_fws ($body) if $self->{option}->{delete_fws}>0;
108 wakaba 1.1 my @b = ();
109     $body =~ s{$REG{FWS}($REG{param})$REG{FWS}(?:;$REG{FWS}|$)}{
110     my $param = $1;
111     push @b, $self->_parse_param ($param);
112     }goex;
113     @b = $self->_restore_param (@b);
114     $self->_save_param (@b);
115     $self;
116     }
117    
118     ## Initialization for parse () method.
119     sub _initialize_parse ($;%) {
120     my $self = shift;
121     #for (keys %DEFAULT) {$self->{option}->{$_} ||= $DEFAULT{$_}}
122     }
123    
124     sub _parse_param ($$) {
125     my $self = shift;
126     my $param = shift;
127     if ($param =~ /^$REG{M_parameter}$/) {
128     my ($name, $value) = (lc $1, $2);
129     my ($seq, $isencoded, $charset, $lang) = (-1, 0, '', '');
130     if ($name =~ /^$REG{M_parameter_name}$/) {
131     ($name, $seq, $isencoded) = ($1, $4?-1:$2, ($3||$4)?1:0);
132     }
133     if ($isencoded && $value =~ /^$REG{M_parameter_extended_value}$/) {
134     ($charset, $lang, $value) = ($1, $2, $3);
135     }
136     return [$name, {value => $value, seq => $seq, is_encoded => $isencoded,
137     charset => $charset, language => $lang, is_parameter => 1}];
138     } else {
139     return [$param, {is_parameter => 0}];
140     }
141     }
142    
143     sub _restore_param ($@) {
144     my $self = shift;
145     my @p = @_;
146     my @ret;
147     my %part;
148     for my $i (@p) {
149     if ($i->[1]->{is_parameter}) {
150     my $p = $i->[1];
151     if ($p->{seq}<0) {
152     my $s = $p->{value};
153     if ($p->{is_encoded}) {
154     $s =~ s/%([0-9A-Fa-f][0-9A-Fa-f])/chr(hex($1))/eg;
155 wakaba 1.3 my %s = &{$self->{option}->{hook_decode_string}} ($self, $s,
156     language => $p->{language}, charset => $p->{charset},
157     type => 'parameter/encoded');
158     ($s, $p->{charset}, $p->{language}) = (@s{qw(value charset language)});
159 wakaba 1.1 } else {
160 wakaba 1.3 my $q = 0;
161     ($s,$q) = $self->_unquote_if_quoted_string ($p->{value});
162     my %s = &{$self->{option}->{hook_decode_string}} ($self, $s,
163     type => ($q?'parameter/quoted':'parameter'));
164     ($s, $p->{charset}, $p->{language}) = (@s{qw(value charset language)});
165 wakaba 1.1 }
166     push @ret, [$i->[0], {value => $s, language => $p->{language},
167     charset => $p->{charset}, is_parameter => 1}];
168     } else {
169     $part{$i->[0]}->[$p->{seq}] = {
170     value => $self->_unquote_if_quoted_string ($p->{value}),
171     language => $p->{language}, charset => $p->{charset},
172     is_encoded => $p->{is_encoded}};
173     }
174 wakaba 1.3 } else {
175     my $q = 0;
176     ($i->[0], $q) = $self->_unquote_if_quoted_string ($i->[0]);
177     my %s = &{$self->{option}->{hook_decode_string}} ($self, $i->[0],
178     type => ($q?'phrase/quoted':'phrase'));
179     ($i->[0]) = ($s{value});
180     push @ret, $i
181     }
182 wakaba 1.1 }
183     for my $name (keys %part) {
184     my $t = join '', map {
185     my $v = $_;
186     my $s = $v->{value};
187     $s =~ s/%([0-9A-Fa-f][0-9A-Fa-f])/chr(hex($1))/eg if $v->{is_encoded};
188     $s;
189     } @{$part{$name}};
190 wakaba 1.3 my %s = &{$self->{option}->{hook_decode_string}} ($self, $t,
191     type => 'parameter/encoded');
192     ($t,@part{$name}->[0]->{qw(charset language)})=(@s{qw(value charset language)});
193 wakaba 1.1 push @ret, [$name, {value => $t, charset => $part{$name}->[0]->{charset},
194     language => $part{$name}->[0]->{language},
195     is_parameter => 1}];
196     }
197     @ret;
198     }
199    
200     sub _save_param ($@) {
201     my $self = shift;
202     my @p = @_;
203     $self->{param} = \@p;
204     $self;
205     }
206    
207     =head2 $self->add ($name, [$value]. [%option]
208    
209     Adds parameter name=value pair.
210    
211     Example:
212     $self->add (title => 'foo of bar'); ## title="foo of bar"
213     $self->add (subject => 'hogehoge, foo'); ## subject*=''hogehoge%2C%20foo
214     $self->add (foo => 'bar', language => 'en') ## foo*='en'bar
215     $self->add ('text/plain', '', value => 1) ## text/plain
216    
217     This method returns array reference of (name, {value => value, attribute...}).
218    
219     Available options: charset (charset name), language (language tag),
220     value (1/0, see example above).
221    
222     =cut
223    
224     sub add ($$;$%) {
225     my $self = shift;
226     my ($name, $value, %option) = (lc shift, shift, @_);
227     my $p = [$name, {value => $value, charset => $option{charset},
228     is_parameter => 1, language => $option{language}}];
229     $p->[1]->{is_parameter} = 0 if !$value && $option{value}>0;
230     croak "add: \$name contains of non-attribute-char: $name"
231 wakaba 1.4 if $p->[1]->{is_parameter} && $name =~ /$REG{NON_http_attribute_char}/;
232 wakaba 1.1 $p->[1]->{value} = $self->_param_value ($name => $p->[1]->{value});
233     if ($option{prepend}) {
234     unshift @{$self->{param}}, $p;
235     } else {
236     push @{$self->{param}}, $p;
237     }
238     $p;
239     }
240     sub replace ($$;$%) {
241     my $self = shift;
242     my ($name, $value, %option) = (lc shift, shift, @_);
243     for my $param (@{$self->{param}}) {
244     if ($param->[0] eq $name) {
245     $param->[1] = {value => $value, charset => $option{charset},
246     is_parameter => 1, language => $option{language}};
247     $param->[1]->{is_parameter} = 0 if !$value && $option{value}>0;
248     $param->[1]->{value} = $self->_param_value ($name => $param->[1]->{value});
249     return $param;
250     }
251     }
252     my $p = [$name, {value => $value, charset => $option{charset},
253     is_parameter => 1, language => $option{language}}];
254     $p->[1]->{is_parameter} = 0 if !$value && $option{value}>0;
255     croak "replace: \$name contains of non-attribute-char: $name"
256 wakaba 1.4 if $p->[1]->{is_parameter} && $name =~ /$REG{NON_http_attribute_char}/;
257 wakaba 1.1 $p->[1]->{value} = $self->_param_value ($name => $p->[1]->{value});
258     push @{$self->{param}}, $p;
259     $p;
260     }
261    
262     sub delete ($$;%) {
263     my $self = shift;
264     my ($name, $index) = (lc shift, shift);
265     my $i = 0;
266     for my $param (@{$self->{param}}) {
267     if ($param->[0] eq $name) {
268     $i++;
269     if ($index == 0 || $i == $index) {
270     undef $param;
271     return $self if $i == $index;
272     }
273     }
274     }
275     $self;
276     }
277    
278     sub count ($;$%) {
279     my $self = shift;
280     my ($name) = (lc shift);
281     unless ($name) {
282     $self->_delete_empty ();
283     return $#{$self->{param}}+1;
284     }
285     my $count = 0;
286     for my $param (@{$self->{param}}) {
287     if ($param->[0] eq $name) {
288     $count++;
289     }
290     }
291     $count;
292     }
293    
294    
295     sub parameter ($$;$) {
296     my $self = shift;
297     my $name = lc shift;
298     my $newvalue = shift;
299     return $self->replace ($name => $newvalue,@_)->[1]->{value} if defined $newvalue;
300     my @ret;
301     for my $param (@{$self->{param}}) {
302     if ($param->[0] eq $name) {
303     unless (wantarray) {
304 wakaba 1.3 $param->[1]->{value}
305     = $self->_param_value ($name => $param->[1]->{value});
306 wakaba 1.1 return $param->[1]->{value};
307     } else {
308 wakaba 1.3 $param->[1]->{value}
309     = $self->_param_value ($name => $param->[1]->{value});
310 wakaba 1.1 push @ret, $param->[1]->{value};
311     }
312     }
313     }
314     @ret;
315     }
316    
317     sub parameter_name ($$;$) {
318     my $self = shift;
319     my $i = shift;
320     my $newname = shift;
321     if ($newname) {
322 wakaba 1.4 return 0 if $newname =~ /$REG{NON_http_attribute_char}/;
323 wakaba 1.1 $self->{param}->[$i]->[0] = $newname;
324     }
325     $self->{param}->[$i]->[0];
326     }
327     sub parameter_value ($$;$) {
328     my $self = shift;
329     my $i = shift;
330     my $newvalue = shift;
331     if ($newvalue) {
332     $newvalue = $self->_param_value ($self->{param}->[$i]->[0] => $newvalue);
333     $self->{param}->[$i]->[1]->{value} = $newvalue;
334     }
335     $self->{param}->[$i]->[1]->{value}
336     = $self->_param_value
337     ($self->{param}->[$i]->[0] => $self->{param}->[$i]->[1]->{value});
338     $self->{param}->[$i]->[1]->{value};
339     }
340    
341     ## Hook called before returning C<value>.
342     ## $self->_param_value ($name, $value);
343     sub _param_value ($$$) {$_[2]}
344    
345     sub _delete_empty ($) {
346     my $self = shift;
347     my @ret;
348     for my $param (@{$self->{param}}) {
349     push @ret, $param if $param->[0];
350     }
351     $self->{param} = \@ret;
352     $self;
353     }
354    
355    
356     =head2 $self->stringify ([%option])
357    
358     Returns Message::Field::Params as a string.
359    
360     =head2 $self->as_string ([%option])
361    
362     An alias of C<stringify>.
363    
364     =cut
365    
366     sub stringify ($;%) {
367     my $self = shift;
368     my %option = @_;
369     my $use_xparam = $option{use_parameter_extension}
370     || $self->{option}->{use_parameter_extension};
371     $option{parameter_value_max}
372     ||= $self->{option}->{parameter_value_max};
373     $self->_delete_empty ();
374     join '; ',
375     map {
376     my $v = $_->[1];
377     my $new = '';
378     if ($v->{is_parameter}) {
379     my ($encoded, @value) = (0, '');
380 wakaba 1.3 my (%e) = &{$self->{option}->{hook_encode_string}} ($self,
381     $v->{value}, current_charset => $v->{charset}, language => $v->{language},
382     type => 'parameter');
383     if ($use_xparam>0 && ($e{charset} || $e{language}
384     || $e{value} =~ /[\x00\x0D\x0A\x80-\xFF]/)) {
385 wakaba 1.1 my ($charset, $lang);
386     $encoded = 1;
387 wakaba 1.3 ($charset, $lang) = ($e{charset}, $e{language});
388 wakaba 1.1 ## Note: %-quoting for charset and for language is not allowed.
389     ## But charset name can be included non-sttribute-char such as "'".
390     ## How can we treat this?
391 wakaba 1.4 $charset =~ s/($REG{NON_http_attribute_char})/sprintf('%%%02X', ord $1)/ge;
392     $lang =~ s/($REG{NON_http_attribute_char})/sprintf('%%%02X', ord $1)/ge;
393 wakaba 1.3 if (length $e{value} > $option{parameter_value_max}) {
394     for my $i (0..length ($e{value})/$option{parameter_value_max}) {
395     $value[$i] = substr ($e{value}, $i*$option{parameter_value_max},
396 wakaba 1.1 $option{parameter_value_max});
397     }
398 wakaba 1.3 } else {$value[0] = $e{value}}
399 wakaba 1.1 for my $i (0..$#value) {
400 wakaba 1.4 $value[$i] =~
401     s/($REG{NON_http_attribute_char})/sprintf('%%%02X', ord $1)/ge;
402 wakaba 1.1 }
403     $value[0] = "${charset}'${lang}'".$value[0];
404 wakaba 1.3 } elsif (length $e{value} == 0) {
405 wakaba 1.1 $value[0] = '""';
406     } else {
407 wakaba 1.3 if ($use_xparam>0 && length $e{value} > $option{parameter_value_max}) {
408     for my $i (0..length ($e{value})/$option{parameter_value_max}) {
409 wakaba 1.1 $value[$i] = $self->_quote_unsafe_string
410 wakaba 1.3 (substr ($e{value}, $i*$option{parameter_value_max},
411 wakaba 1.4 $option{parameter_value_max}),
412     unsafe => 'NON_http_attribute_char');
413 wakaba 1.1 }
414     } else {
415     $value[0] = $self->_quote_unsafe_string
416 wakaba 1.4 ($e{value}, unsafe => 'NON_http_attribute_char');
417 wakaba 1.1 }
418     }
419     ## Note: quoted-string for parameter name is not allowed.
420     ## But it is better than output bare non-atext.
421     if ($#value == 0) {
422     $new =
423     $self->_quote_unsafe_string ($_->[0], unsafe => 'NON_attribute_char')
424     .($encoded?'*':'').'='.$value[0];
425     } else {
426     my @new;
427     my $name = $self->_quote_unsafe_string
428 wakaba 1.4 ($_->[0], unsafe => 'NON_http_attribute_char');
429 wakaba 1.1 for my $i (0..$#value) {
430     push @new, $name.'*'.$i.($encoded?'*':'').'='.$value[$i];
431     }
432     $new = join '; ', @new;
433     }
434     } else {
435 wakaba 1.3 my %e = &{$self->{option}->{hook_encode_string}} ($self,
436     $_->[0], type => 'phrase');
437 wakaba 1.4 $new = $self->_quote_unsafe_string ($e{value},
438     unsafe => 'NON_http_token_wsp');
439 wakaba 1.1 }
440     $new;
441     } @{$self->{param}}
442     ;
443     }
444     sub as_string ($;%) {shift->stringify (@_)}
445    
446     =head2 $self->option ($option_name)
447    
448     Returns/set (new) value of the option.
449    
450     =cut
451    
452     sub option ($$;$) {
453     my $self = shift;
454     my ($name, $newval) = @_;
455     if ($newval) {
456     $self->{option}->{$name} = $newval;
457     }
458     $self->{option}->{$name};
459     }
460    
461     sub _quote_unsafe_string ($$;%) {
462     my $self = shift;
463     my $string = shift;
464     my %option = @_;
465     $option{unsafe} ||= 'NON_atext_dot';
466     if ($string =~ /$REG{$option{unsafe}}/ || $string =~ /$REG{WSP}$REG{WSP}+/) {
467 wakaba 1.4 $string =~ s/([\x22\x5C])([\x21-\x7E])?/"\x5C$1".(defined $2?"\x5C$2":'')/ge;
468 wakaba 1.1 $string = '"'.$string.'"';
469     }
470     $string;
471     }
472    
473     =head2 $self->_unquote_quoted_string ($string)
474    
475     Unquote C<quoted-string>. Get rid of C<DQUOTE>s and
476     C<REVERSED SOLIDUS> included in C<quoted-pair>.
477     This method is intended for internal use.
478    
479     =cut
480    
481     sub _unquote_quoted_string ($$) {
482     my $self = shift;
483     my $quoted_string = shift;
484     $quoted_string =~ s{$REG{M_quoted_string}}{
485     my $qtext = $1;
486     $qtext =~ s/\x5C([\x00-\xFF])/$1/g;
487     $qtext;
488     }goex;
489     $quoted_string;
490     }
491    
492 wakaba 1.4 ## Unquote C<DQOUTE> and C<quoted-pair> if it is itself a
493     ## C<quoted-string>. (Do nothing if it is MULTIPLE
494     ## C<quoted-string>"S".)
495 wakaba 1.1 sub _unquote_if_quoted_string ($$) {
496     my $self = shift;
497 wakaba 1.3 my $quoted_string = shift; my $isq = 0;
498 wakaba 1.1 $quoted_string =~ s{^$REG{M_quoted_string}$}{
499     my $qtext = $1;
500     $qtext =~ s/\x5C([\x00-\xFF])/$1/g;
501 wakaba 1.3 $isq = 1;
502 wakaba 1.1 $qtext;
503     }goex;
504 wakaba 1.3 wantarray? ($quoted_string, $isq): $quoted_string;
505 wakaba 1.1 }
506    
507     =head2 $self->_delete_comment ($field_body)
508    
509     Remove all C<comment> in given strictured C<field-body>.
510     This method is intended for internal use.
511    
512     =cut
513    
514     sub _delete_comment ($$) {
515     my $self = shift;
516     my $body = shift;
517     $body =~ s{($REG{quoted_string}|$REG{domain_literal})|$REG{comment}}{
518     my $o = $1; $o? $o : ' ';
519     }gex;
520     $body;
521     }
522    
523     sub _delete_fws ($$) {
524     my $self = shift;
525     my $body = shift;
526 wakaba 1.3 $body =~ s{($REG{quoted_string}|$REG{domain_literal})|((?:$REG{token}|$REG{S_encoded_word})(?:$REG{WSP}+(?:$REG{token}|$REG{S_encoded_word}))+)|$REG{WSP}+}{
527     my ($o,$p) = ($1,$2);
528     if ($o) {$o}
529     elsif ($p) {$p=~s/$REG{WSP}+/\x20/g;$p}
530     else {''}
531 wakaba 1.1 }gex;
532     $body;
533     }
534    
535     =head1 LICENSE
536    
537     Copyright 2002 wakaba E<lt>w@suika.fam.cxE<gt>.
538    
539     This program is free software; you can redistribute it and/or modify
540     it under the terms of the GNU General Public License as published by
541     the Free Software Foundation; either version 2 of the License, or
542     (at your option) any later version.
543    
544     This program is distributed in the hope that it will be useful,
545     but WITHOUT ANY WARRANTY; without even the implied warranty of
546     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
547     GNU General Public License for more details.
548    
549     You should have received a copy of the GNU General Public License
550     along with this program; see the file COPYING. If not, write to
551     the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
552     Boston, MA 02111-1307, USA.
553    
554     =head1 CHANGE
555    
556     See F<ChangeLog>.
557 wakaba 1.4 $Date: 2002/03/25 10:15:26 $
558 wakaba 1.1
559     =cut
560    
561     1;

admin@suikawiki.org
ViewVC Help
Powered by ViewVC 1.1.24