File Coverage

blib/lib/CGI/Simple/Util.pm
Criterion Covered Total %
statement 116 150 77.3
branch 60 96 62.5
condition 15 29 51.7
subroutine 17 18 94.4
pod 0 13 0.0
total 208 306 67.9


line stmt bran cond sub pod time code
1             package CGI::Simple::Util;
2 23     23   291146 use strict;
  23         65  
  23         988  
3 23     23   140 use warnings;
  23         41  
  23         1781  
4 23     23   146 use vars qw( $VERSION @EXPORT_OK @ISA $UTIL );
  23         53  
  23         85642  
5             $VERSION = '1.282';
6             require Exporter;
7             @ISA = qw( Exporter );
8             @EXPORT_OK = qw(
9             rearrange make_attributes expires
10             escapeHTML unescapeHTML escape unescape
11             );
12              
13             sub rearrange {
14 223     223 0 586 my ( $order, @params ) = @_;
15 223         359 my ( %pos, @result, %leftover );
16 223 100       646 return () unless @params;
17 192 50       525 if ( ref $params[0] eq 'HASH' ) {
18 0         0 @params = %{ $params[0] };
  0         0  
19             }
20             else {
21 192 100       737 return @params unless $params[0] =~ m/^-/;
22             }
23              
24             # map parameters into positional indices
25 169         249 my $i = 0;
26 169         399 for ( @$order ) {
27 1382 100       2560 for ( ref( $_ ) eq 'ARRAY' ? @$_ : $_ ) { $pos{ lc( $_ ) } = $i; }
  1736         3447  
28 1382         1881 $i++;
29             }
30 169         613 $#result = $#$order; # preextend
31 169         410 while ( @params ) {
32 488         975 my $key = lc( shift( @params ) );
33 488         1217 $key =~ s/^\-//;
34 488 100       1008 if ( exists $pos{$key} ) {
35 455         1406 $result[ $pos{$key} ] = shift( @params );
36             }
37             else {
38 33         106 $leftover{$key} = shift( @params );
39             }
40             }
41 169 100       419 push @result, make_attributes( \%leftover, 1 ) if %leftover;
42 169         1306 return @result;
43             }
44              
45             sub make_attributes {
46 32     32 0 53 my $attref = shift;
47 32   50     115 my $escape = shift || 0;
48 32 50 33     172 return () unless $attref && ref $attref eq 'HASH';
49 32         48 my @attrib;
50 32         43 for my $key ( keys %{$attref} ) {
  32         86  
51 33         73 ( my $mod_key = $key ) =~ s/^-//; # get rid of initial - if present
52 33         61 $mod_key = lc $mod_key; # parameters are lower case
53 33         94 $mod_key =~ tr/_/-/; # use dashes
54             my $value
55 33 50       171 = $escape ? escapeHTML( $attref->{$key} ) : $attref->{$key};
56 33 50       133 push @attrib, defined $value ? qq/$mod_key="$value"/ : $mod_key;
57             }
58 32         109 return @attrib;
59             }
60              
61             # This internal routine creates date strings suitable for use in
62             # cookies and HTTP headers. (They differ, unfortunately.)
63             # Thanks to Mark Fisher for this.
64             sub expires {
65 43     43 0 113 my ( $time, $format ) = @_;
66 43   50     106 $format ||= 'http';
67 43         173 my @MON = qw( Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec);
68 43         120 my @WDAY = qw( Sun Mon Tue Wed Thu Fri Sat );
69              
70             # pass through preformatted dates for the sake of expire_calc()
71 43         94 $time = _expire_calc( $time );
72 43 100       297 return $time unless $time =~ /^\d+$/;
73              
74             # make HTTP/cookie date string from GMT'ed time
75             # (cookies use '-' as date separator, HTTP uses ' ')
76 32 100       109 my $sc = $format eq 'cookie' ? '-' : ' ';
77 32         167 my ( $sec, $min, $hour, $mday, $mon, $year, $wday ) = gmtime( $time );
78 32         97 $year += 1900;
79 32         312 return sprintf( "%s, %02d$sc%s$sc%04d %02d:%02d:%02d GMT",
80             $WDAY[$wday], $mday, $MON[$mon], $year, $hour, $min, $sec );
81             }
82              
83             # This internal routine creates an expires time exactly some number of
84             # hours from the current time. It incorporates modifications from Mark Fisher.
85             # format for time can be in any of the forms...
86             # "now" -- expire immediately
87             # "+180s" -- in 180 seconds
88             # "+2m" -- in 2 minutes
89             # "+12h" -- in 12 hours
90             # "+1d" -- in 1 day
91             # "+3M" -- in 3 months
92             # "+2y" -- in 2 years
93             # "-3m" -- 3 minutes ago(!)
94             # If you don't supply one of these forms, we assume you are specifying
95             # the date yourself
96             sub _expire_calc {
97 48     48   109 my ( $time ) = @_;
98 48         261 my %mult = (
99             's' => 1,
100             'm' => 60,
101             'h' => 60 * 60,
102             'd' => 60 * 60 * 24,
103             'M' => 60 * 60 * 24 * 30,
104             'y' => 60 * 60 * 24 * 365
105             );
106 48         183 my $offset;
107 48 100 100     362 if ( !$time or lc $time eq 'now' ) {
    100          
    100          
108 27         50 $offset = 0;
109             }
110             elsif ( $time =~ /^\d+/ ) {
111 1         4 return $time;
112             }
113             elsif ( $time =~ /^([+-]?(?:\d+|\d*\.\d*))([mhdMy]?)/ ) {
114 9   50     46 $offset = ( $mult{$2} || 1 ) * $1;
115             }
116             else {
117 11         46 return $time;
118             }
119 36         89 my $cur_time = time;
120 36         141 return ( $cur_time + $offset );
121             }
122              
123             sub escapeHTML {
124 51     51 0 124 my ( $escape, $text ) = @_;
125 51 100       139 return undef unless defined $escape;
126 49         158 $escape =~ s/&/&/g;
127 49         92 $escape =~ s/"/"/g;
128 49         107 $escape =~ s/
129 49         322 $escape =~ s/>/>/g;
130              
131             # these next optional escapes make text look the same when rendered in HTML
132 49 50       126 if ( $text ) {
133 0         0 $escape =~ s/\t/ /g; # tabs to 4 spaces
134 0         0 $escape =~ s/( {2,})/" " x length $1/eg; # whitespace escapes
  0         0  
135 0         0 $escape =~ s/\n/
\n/g; # newlines to
136             }
137 49         136 return $escape;
138             }
139              
140             sub unescapeHTML {
141 137     137 0 206 my ( $unescape ) = @_;
142 137 100       254 return undef unless defined( $unescape );
143 124         366 my $latin = $UTIL->{'charset'} =~ /^(?:ISO-8859-1|WINDOWS-1252)$/i;
144 124         177 my $ebcdic = $UTIL->{'ebcdic'};
145              
146             # credit to Randal Schwartz for original version of this
147 124         181 $unescape =~ s[&(.*?);]{
148 27         62 local $_ = $1;
149             /^amp$/i ? "&" :
150             /^quot$/i ? '"' :
151             /^gt$/i ? ">" :
152             /^lt$/i ? "<" :
153             /^#(\d+)$/ && $latin ? chr($1) :
154             /^#(\d+)$/ && $ebcdic ? chr($UTIL->{'a2e'}->[$1]) :
155             /^#x([0-9a-f]+)$/i && $latin ? chr(hex($1)) :
156 27 50 66     217 /^#x([0-9a-f]+)$/i && $ebcdic ? chr($UTIL->{'a2e'}->[hex $1]) :
    50 33        
    50 33        
    100 33        
    100          
    100          
    100          
    100          
157             "\&$_;"
158             }gex;
159 124         319 return $unescape;
160             }
161              
162             # URL-encode data
163             sub escape {
164 253     253 0 266451 my ( $toencode ) = @_;
165 253 50       529 return undef unless defined $toencode;
166 253 50       614 if ( $UTIL->{'ebcdic'} ) {
167 0         0 $toencode
168 0         0 =~ s/([^a-zA-Z0-9_.-])/uc sprintf "%%%02x", $UTIL->{'e2a'}->[ord $1]/eg;
169             }
170             else {
171 253         816 $toencode =~ s/([^a-zA-Z0-9_.-])/uc sprintf "%%%02x", ord $1 /eg;
  84         555  
172             }
173 253         878 return $toencode;
174             }
175              
176             # unescape URL-encoded data
177             sub unescape {
178 118     118 0 27033 my ( $todecode ) = @_;
179 118 50       333 return undef unless defined $todecode;
180 118         234 $todecode =~ tr/+/ /;
181 118 50       316 if ( $UTIL->{'ebcdic'} ) {
182 0         0 $todecode =~ s/%([0-9a-fA-F]{2})/chr $UTIL->{'a2e'}->[hex $1]/ge;
  0         0  
183             }
184             else {
185 118         436 $todecode =~ s/%(?:([0-9a-fA-F]{2})|u([0-9a-fA-F]{4}))/
186 45 50       288 defined($1)? chr hex($1) : utf8_chr(hex($2))/ge;
187             }
188 118         415 return $todecode;
189             }
190              
191             sub utf8_chr ($) {
192 0     0 0 0 my $c = shift;
193 0 0       0 if ( $c < 0x80 ) {
    0          
    0          
    0          
    0          
    0          
194 0         0 return sprintf( "%c", $c );
195             }
196             elsif ( $c < 0x800 ) {
197 0         0 return sprintf( "%c%c", 0xc0 | ( $c >> 6 ), 0x80 | ( $c & 0x3f ) );
198             }
199             elsif ( $c < 0x10000 ) {
200 0         0 return sprintf( "%c%c%c",
201             0xe0 | ( $c >> 12 ),
202             0x80 | ( ( $c >> 6 ) & 0x3f ),
203             0x80 | ( $c & 0x3f ) );
204             }
205             elsif ( $c < 0x200000 ) {
206 0         0 return sprintf( "%c%c%c%c",
207             0xf0 | ( $c >> 18 ),
208             0x80 | ( ( $c >> 12 ) & 0x3f ),
209             0x80 | ( ( $c >> 6 ) & 0x3f ),
210             0x80 | ( $c & 0x3f ) );
211             }
212             elsif ( $c < 0x4000000 ) {
213 0         0 return sprintf( "%c%c%c%c%c",
214             0xf8 | ( $c >> 24 ),
215             0x80 | ( ( $c >> 18 ) & 0x3f ),
216             0x80 | ( ( $c >> 12 ) & 0x3f ),
217             0x80 | ( ( $c >> 6 ) & 0x3f ),
218             0x80 | ( $c & 0x3f ) );
219              
220             }
221             elsif ( $c < 0x80000000 ) {
222 0         0 return sprintf(
223             "%c%c%c%c%c%c",
224             0xfc | ( $c >> 30 ), # was 0xfe patch Thomas L. Shinnick
225             0x80 | ( ( $c >> 24 ) & 0x3f ),
226             0x80 | ( ( $c >> 18 ) & 0x3f ),
227             0x80 | ( ( $c >> 12 ) & 0x3f ),
228             0x80 | ( ( $c >> 6 ) & 0x3f ),
229             0x80 | ( $c & 0x3f )
230             );
231             }
232             else {
233 0         0 return utf8( 0xfffd );
234             }
235             }
236              
237             # We need to define a number of things about the operating environment so
238             # we do this on first initialization and store the results in in an object
239 0         0 BEGIN {
240              
241 23     23   284 $UTIL = CGI::Simple::Util->new; # initialize our $UTIL object
242              
243             sub new {
244 23     23 0 91 my $class = shift;
245 23   33     199 $class = ref( $class ) || $class;
246 23         66 my $self = {};
247 23         60 bless $self, $class;
248 23         172 $self->init;
249 23         1021 return $self;
250             }
251              
252             sub init {
253 23     23 0 51 my $self = shift;
254 23         89 $self->charset;
255 23         135 $self->os;
256 23         83 $self->ebcdic;
257             }
258              
259             sub charset {
260 70     70 0 193 my ( $self, $charset ) = @_;
261 70 100       265 $self->{'charset'} = $charset if $charset;
262 70   100     708 $self->{'charset'}
263             ||= 'ISO-8859-1'; # set to the safe ISO-8859-1 if not defined
264 70         240 return $self->{'charset'};
265             }
266              
267             sub os {
268 23     23 0 109 my ( $self, $OS ) = @_;
269 23 50       88 $self->{'os'} = $OS if $OS; # allow value to be set manually
270 23         60 $OS = $self->{'os'};
271 23 50       97 unless ( $OS ) {
272 23 50       165 unless ( $OS = $^O ) {
273 0         0 require Config;
274 0         0 $OS = $Config::Config{'osname'};
275             }
276 23 50       470 if ( $OS =~ /Win/i ) {
    50          
    50          
    50          
    50          
    50          
277 0         0 $OS = 'WINDOWS';
278             }
279             elsif ( $OS =~ /vms/i ) {
280 0         0 $OS = 'VMS';
281             }
282             elsif ( $OS =~ /bsdos/i ) {
283 0         0 $OS = 'UNIX';
284             }
285             elsif ( $OS =~ /dos/i ) {
286 0         0 $OS = 'DOS';
287             }
288             elsif ( $OS =~ /^MacOS$/i ) {
289 0         0 $OS = 'MACINTOSH';
290             }
291             elsif ( $OS =~ /os2/i ) {
292 0         0 $OS = 'OS2';
293             }
294             else {
295 23         154 $OS = 'UNIX';
296             }
297             }
298 23         101 return $self->{'os'} = $OS;
299             }
300              
301             sub ebcdic {
302 23     23 0 44 my $self = shift;
303 23 50       90 return $self->{'ebcdic'} if exists $self->{'ebcdic'};
304 23         45 $self->{'ebcdic'} = "\t" ne "\011" ? 1 : 0;
305 23 50       88 if ( $self->{'ebcdic'} ) {
306              
307             # (ord('^') == 95) for codepage 1047 as on os390, vmesa
308 0           my @A2E = (
309             0, 1, 2, 3, 55, 45, 46, 47, 22, 5, 21, 11,
310             12, 13, 14, 15, 16, 17, 18, 19, 60, 61, 50, 38,
311             24, 25, 63, 39, 28, 29, 30, 31, 64, 90, 127, 123,
312             91, 108, 80, 125, 77, 93, 92, 78, 107, 96, 75, 97,
313             240, 241, 242, 243, 244, 245, 246, 247, 248, 249, 122, 94,
314             76, 126, 110, 111, 124, 193, 194, 195, 196, 197, 198, 199,
315             200, 201, 209, 210, 211, 212, 213, 214, 215, 216, 217, 226,
316             227, 228, 229, 230, 231, 232, 233, 173, 224, 189, 95, 109,
317             121, 129, 130, 131, 132, 133, 134, 135, 136, 137, 145, 146,
318             147, 148, 149, 150, 151, 152, 153, 162, 163, 164, 165, 166,
319             167, 168, 169, 192, 79, 208, 161, 7, 32, 33, 34, 35,
320             36, 37, 6, 23, 40, 41, 42, 43, 44, 9, 10, 27,
321             48, 49, 26, 51, 52, 53, 54, 8, 56, 57, 58, 59,
322             4, 20, 62, 255, 65, 170, 74, 177, 159, 178, 106, 181,
323             187, 180, 154, 138, 176, 202, 175, 188, 144, 143, 234, 250,
324             190, 160, 182, 179, 157, 218, 155, 139, 183, 184, 185, 171,
325             100, 101, 98, 102, 99, 103, 158, 104, 116, 113, 114, 115,
326             120, 117, 118, 119, 172, 105, 237, 238, 235, 239, 236, 191,
327             128, 253, 254, 251, 252, 186, 174, 89, 68, 69, 66, 70,
328             67, 71, 156, 72, 84, 81, 82, 83, 88, 85, 86, 87,
329             140, 73, 205, 206, 203, 207, 204, 225, 112, 221, 222, 219,
330             220, 141, 142, 223
331             );
332 0           my @E2A = (
333             0, 1, 2, 3, 156, 9, 134, 127, 151, 141, 142, 11,
334             12, 13, 14, 15, 16, 17, 18, 19, 157, 10, 8, 135,
335             24, 25, 146, 143, 28, 29, 30, 31, 128, 129, 130, 131,
336             132, 133, 23, 27, 136, 137, 138, 139, 140, 5, 6, 7,
337             144, 145, 22, 147, 148, 149, 150, 4, 152, 153, 154, 155,
338             20, 21, 158, 26, 32, 160, 226, 228, 224, 225, 227, 229,
339             231, 241, 162, 46, 60, 40, 43, 124, 38, 233, 234, 235,
340             232, 237, 238, 239, 236, 223, 33, 36, 42, 41, 59, 94,
341             45, 47, 194, 196, 192, 193, 195, 197, 199, 209, 166, 44,
342             37, 95, 62, 63, 248, 201, 202, 203, 200, 205, 206, 207,
343             204, 96, 58, 35, 64, 39, 61, 34, 216, 97, 98, 99,
344             100, 101, 102, 103, 104, 105, 171, 187, 240, 253, 254, 177,
345             176, 106, 107, 108, 109, 110, 111, 112, 113, 114, 170, 186,
346             230, 184, 198, 164, 181, 126, 115, 116, 117, 118, 119, 120,
347             121, 122, 161, 191, 208, 91, 222, 174, 172, 163, 165, 183,
348             169, 167, 182, 188, 189, 190, 221, 168, 175, 93, 180, 215,
349             123, 65, 66, 67, 68, 69, 70, 71, 72, 73, 173, 244,
350             246, 242, 243, 245, 125, 74, 75, 76, 77, 78, 79, 80,
351             81, 82, 185, 251, 252, 249, 250, 255, 92, 247, 83, 84,
352             85, 86, 87, 88, 89, 90, 178, 212, 214, 210, 211, 213,
353             48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 179, 219,
354             220, 217, 218, 159
355             );
356 0           if ( ord( '^' ) == 106 )
357             { # as in the BS2000 posix-bc coded character set
358             $A2E[91] = 187;
359             $A2E[92] = 188;
360             $A2E[94] = 106;
361             $A2E[96] = 74;
362             $A2E[123] = 251;
363             $A2E[125] = 253;
364             $A2E[126] = 255;
365             $A2E[159] = 95;
366             $A2E[162] = 176;
367             $A2E[166] = 208;
368             $A2E[168] = 121;
369             $A2E[172] = 186;
370             $A2E[175] = 161;
371             $A2E[217] = 224;
372             $A2E[219] = 221;
373             $A2E[221] = 173;
374             $A2E[249] = 192;
375              
376             $E2A[74] = 96;
377             $E2A[95] = 159;
378             $E2A[106] = 94;
379             $E2A[121] = 168;
380             $E2A[161] = 175;
381             $E2A[173] = 221;
382             $E2A[176] = 162;
383             $E2A[186] = 172;
384             $E2A[187] = 91;
385             $E2A[188] = 92;
386             $E2A[192] = 249;
387             $E2A[208] = 166;
388             $E2A[221] = 219;
389             $E2A[224] = 217;
390             $E2A[251] = 123;
391             $E2A[253] = 125;
392             $E2A[255] = 126;
393             }
394 0           elsif ( ord( '^' ) == 176 ) { # as in codepage 037 on os400
395             $A2E[10] = 37;
396             $A2E[91] = 186;
397             $A2E[93] = 187;
398             $A2E[94] = 176;
399             $A2E[133] = 21;
400             $A2E[168] = 189;
401             $A2E[172] = 95;
402             $A2E[221] = 173;
403              
404             $E2A[21] = 133;
405             $E2A[37] = 10;
406             $E2A[95] = 172;
407             $E2A[173] = 221;
408             $E2A[176] = 94;
409             $E2A[186] = 91;
410             $E2A[187] = 93;
411             $E2A[189] = 168;
412             }
413 0           $self->{'a2e'} = \@A2E;
414 0           $self->{'e2a'} = \@E2A;
415             }
416             }
417             }
418              
419             1;
420              
421             __END__