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 21     21   162387 use strict;
  21         66  
  21         651  
3 21     21   106 use warnings;
  21         40  
  21         772  
4 21     21   111 use vars qw( $VERSION @EXPORT_OK @ISA $UTIL );
  21         40  
  21         56027  
5             $VERSION = '1.27';
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 216     216 0 531 my ( $order, @params ) = @_;
15 216         347 my ( %pos, @result, %leftover );
16 216 100       554 return () unless @params;
17 185 50       451 if ( ref $params[0] eq 'HASH' ) {
18 0         0 @params = %{ $params[0] };
  0         0  
19             }
20             else {
21 185 100       853 return @params unless $params[0] =~ m/^-/;
22             }
23              
24             # map parameters into positional indices
25 162         255 my $i = 0;
26 162         317 for ( @$order ) {
27 1215 100       2132 for ( ref( $_ ) eq 'ARRAY' ? @$_ : $_ ) { $pos{ lc( $_ ) } = $i; }
  1541         2685  
28 1215         1566 $i++;
29             }
30 162         495 $#result = $#$order; # preextend
31 162         390 while ( @params ) {
32 477         850 my $key = lc( shift( @params ) );
33 477         1282 $key =~ s/^\-//;
34 477 100       1041 if ( exists $pos{$key} ) {
35 451         1156 $result[ $pos{$key} ] = shift( @params );
36             }
37             else {
38 26         77 $leftover{$key} = shift( @params );
39             }
40             }
41 162 100       379 push @result, make_attributes( \%leftover, 1 ) if %leftover;
42 162         1110 return @result;
43             }
44              
45             sub make_attributes {
46 25     25 0 38 my $attref = shift;
47 25   50     52 my $escape = shift || 0;
48 25 50 33     113 return () unless $attref && ref $attref eq 'HASH';
49 25         42 my @attrib;
50 25         36 for my $key ( keys %{$attref} ) {
  25         79  
51 26         55 ( my $mod_key = $key ) =~ s/^-//; # get rid of initial - if present
52 26         43 $mod_key = lc $mod_key; # parameters are lower case
53 26         51 $mod_key =~ tr/_/-/; # use dashes
54             my $value
55 26 50       81 = $escape ? escapeHTML( $attref->{$key} ) : $attref->{$key};
56 26 50       105 push @attrib, defined $value ? qq/$mod_key="$value"/ : $mod_key;
57             }
58 25         67 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 102 my ( $time, $format ) = @_;
66 43   50     90 $format ||= 'http';
67 43         169 my @MON = qw( Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec);
68 43         97 my @WDAY = qw( Sun Mon Tue Wed Thu Fri Sat );
69              
70             # pass through preformatted dates for the sake of expire_calc()
71 43         93 $time = _expire_calc( $time );
72 43 100       230 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       84 my $sc = $format eq 'cookie' ? '-' : ' ';
77 32         237 my ( $sec, $min, $hour, $mday, $mon, $year, $wday ) = gmtime( $time );
78 32         77 $year += 1900;
79 32         287 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   84 my ( $time ) = @_;
98 48         207 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         76 my $offset;
107 48 100 100     280 if ( !$time or lc $time eq 'now' ) {
    100          
    100          
108 27         46 $offset = 0;
109             }
110             elsif ( $time =~ /^\d+/ ) {
111 1         7 return $time;
112             }
113             elsif ( $time =~ /^([+-]?(?:\d+|\d*\.\d*))([mhdMy]?)/ ) {
114 9   50     60 $offset = ( $mult{$2} || 1 ) * $1;
115             }
116             else {
117 11         46 return $time;
118             }
119 36         80 my $cur_time = time;
120 36         132 return ( $cur_time + $offset );
121             }
122              
123             sub escapeHTML {
124 44     44 0 123 my ( $escape, $text ) = @_;
125 44 100       101 return undef unless defined $escape;
126 42         125 $escape =~ s/&/&/g;
127 42         72 $escape =~ s/"/"/g;
128 42         67 $escape =~ s/
129 42         267 $escape =~ s/>/>/g;
130              
131             # these next optional escapes make text look the same when rendered in HTML
132 42 50       102 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 42         120 return $escape;
138             }
139              
140             sub unescapeHTML {
141 135     135 0 222 my ( $unescape ) = @_;
142 135 100       309 return undef unless defined( $unescape );
143 122         335 my $latin = $UTIL->{'charset'} =~ /^(?:ISO-8859-1|WINDOWS-1252)$/i;
144 122         178 my $ebcdic = $UTIL->{'ebcdic'};
145              
146             # credit to Randal Schwartz for original version of this
147 122         187 $unescape =~ s[&(.*?);]{
148 27         65 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     193 /^#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 122         317 return $unescape;
160             }
161              
162             # URL-encode data
163             sub escape {
164 245     245 0 24917 my ( $toencode ) = @_;
165 245 50       463 return undef unless defined $toencode;
166 245 50       461 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 245         688 $toencode =~ s/([^a-zA-Z0-9_.-])/uc sprintf "%%%02x", ord $1 /eg;
  84         518  
172             }
173 245         676 return $toencode;
174             }
175              
176             # unescape URL-encoded data
177             sub unescape {
178 118     118 0 24667 my ( $todecode ) = @_;
179 118 50       242 return undef unless defined $todecode;
180 118         194 $todecode =~ tr/+/ /;
181 118 50       253 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         357 $todecode =~ s/%(?:([0-9a-fA-F]{2})|u([0-9a-fA-F]{4}))/
186 45 50       227 defined($1)? chr hex($1) : utf8_chr(hex($2))/ge;
187             }
188 118         302 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 21     21   300 $UTIL = CGI::Simple::Util->new; # initialize our $UTIL object
242              
243             sub new {
244 21     21 0 65 my $class = shift;
245 21   33     150 $class = ref( $class ) || $class;
246 21         59 my $self = {};
247 21         68 bless $self, $class;
248 21         62 $self->init;
249 21         780 return $self;
250             }
251              
252             sub init {
253 21     21 0 30 my $self = shift;
254 21         65 $self->charset;
255 21         62 $self->os;
256 21         59 $self->ebcdic;
257             }
258              
259             sub charset {
260 66     66 0 155 my ( $self, $charset ) = @_;
261 66 100       204 $self->{'charset'} = $charset if $charset;
262 66   100     449 $self->{'charset'}
263             ||= 'ISO-8859-1'; # set to the safe ISO-8859-1 if not defined
264 66         161 return $self->{'charset'};
265             }
266              
267             sub os {
268 21     21 0 57 my ( $self, $OS ) = @_;
269 21 50       70 $self->{'os'} = $OS if $OS; # allow value to be set manually
270 21         39 $OS = $self->{'os'};
271 21 50       55 unless ( $OS ) {
272 21 50       107 unless ( $OS = $^O ) {
273 0         0 require Config;
274 0         0 $OS = $Config::Config{'osname'};
275             }
276 21 50       343 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 21         50 $OS = 'UNIX';
296             }
297             }
298 21         58 return $self->{'os'} = $OS;
299             }
300              
301             sub ebcdic {
302 21     21 0 45 my $self = shift;
303 21 50       61 return $self->{'ebcdic'} if exists $self->{'ebcdic'};
304 21         54 $self->{'ebcdic'} = "\t" ne "\011" ? 1 : 0;
305 21 50       82 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__