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   148147 use strict;
  21         59  
  21         690  
3 21     21   131 use warnings;
  21         42  
  21         721  
4 21     21   115 use vars qw( $VERSION @EXPORT_OK @ISA $UTIL );
  21         45  
  21         56776  
5             $VERSION = '1.280';
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 516 my ( $order, @params ) = @_;
15 216         330 my ( %pos, @result, %leftover );
16 216 100       528 return () unless @params;
17 185 50       436 if ( ref $params[0] eq 'HASH' ) {
18 0         0 @params = %{ $params[0] };
  0         0  
19             }
20             else {
21 185 100       799 return @params unless $params[0] =~ m/^-/;
22             }
23              
24             # map parameters into positional indices
25 162         234 my $i = 0;
26 162         359 for ( @$order ) {
27 1215 100       2023 for ( ref( $_ ) eq 'ARRAY' ? @$_ : $_ ) { $pos{ lc( $_ ) } = $i; }
  1541         2528  
28 1215         1574 $i++;
29             }
30 162         444 $#result = $#$order; # preextend
31 162         360 while ( @params ) {
32 477         741 my $key = lc( shift( @params ) );
33 477         1216 $key =~ s/^\-//;
34 477 100       955 if ( exists $pos{$key} ) {
35 451         1059 $result[ $pos{$key} ] = shift( @params );
36             }
37             else {
38 26         71 $leftover{$key} = shift( @params );
39             }
40             }
41 162 100       384 push @result, make_attributes( \%leftover, 1 ) if %leftover;
42 162         910 return @result;
43             }
44              
45             sub make_attributes {
46 25     25 0 36 my $attref = shift;
47 25   50     58 my $escape = shift || 0;
48 25 50 33     136 return () unless $attref && ref $attref eq 'HASH';
49 25         41 my @attrib;
50 25         30 for my $key ( keys %{$attref} ) {
  25         80  
51 26         48 ( my $mod_key = $key ) =~ s/^-//; # get rid of initial - if present
52 26         48 $mod_key = lc $mod_key; # parameters are lower case
53 26         47 $mod_key =~ tr/_/-/; # use dashes
54             my $value
55 26 50       88 = $escape ? escapeHTML( $attref->{$key} ) : $attref->{$key};
56 26 50       112 push @attrib, defined $value ? qq/$mod_key="$value"/ : $mod_key;
57             }
58 25         56 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 77 my ( $time, $format ) = @_;
66 43   50     77 $format ||= 'http';
67 43         141 my @MON = qw( Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec);
68 43         83 my @WDAY = qw( Sun Mon Tue Wed Thu Fri Sat );
69              
70             # pass through preformatted dates for the sake of expire_calc()
71 43         92 $time = _expire_calc( $time );
72 43 100       200 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       76 my $sc = $format eq 'cookie' ? '-' : ' ';
77 32         213 my ( $sec, $min, $hour, $mday, $mon, $year, $wday ) = gmtime( $time );
78 32         73 $year += 1900;
79 32         247 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   76 my ( $time ) = @_;
98 48         165 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         66 my $offset;
107 48 100 100     243 if ( !$time or lc $time eq 'now' ) {
    100          
    100          
108 27         38 $offset = 0;
109             }
110             elsif ( $time =~ /^\d+/ ) {
111 1         5 return $time;
112             }
113             elsif ( $time =~ /^([+-]?(?:\d+|\d*\.\d*))([mhdMy]?)/ ) {
114 9   50     42 $offset = ( $mult{$2} || 1 ) * $1;
115             }
116             else {
117 11         39 return $time;
118             }
119 36         72 my $cur_time = time;
120 36         110 return ( $cur_time + $offset );
121             }
122              
123             sub escapeHTML {
124 44     44 0 142 my ( $escape, $text ) = @_;
125 44 100       100 return undef unless defined $escape;
126 42         121 $escape =~ s/&/&/g;
127 42         79 $escape =~ s/"/"/g;
128 42         63 $escape =~ s/
129 42         190 $escape =~ s/>/>/g;
130              
131             # these next optional escapes make text look the same when rendered in HTML
132 42 50       100 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         99 return $escape;
138             }
139              
140             sub unescapeHTML {
141 135     135 0 231 my ( $unescape ) = @_;
142 135 100       278 return undef unless defined( $unescape );
143 122         408 my $latin = $UTIL->{'charset'} =~ /^(?:ISO-8859-1|WINDOWS-1252)$/i;
144 122         155 my $ebcdic = $UTIL->{'ebcdic'};
145              
146             # credit to Randal Schwartz for original version of this
147 122         197 $unescape =~ s[&(.*?);]{
148 27         52 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     180 /^#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         318 return $unescape;
160             }
161              
162             # URL-encode data
163             sub escape {
164 245     245 0 15192 my ( $toencode ) = @_;
165 245 50       437 return undef unless defined $toencode;
166 245 50       467 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         695 $toencode =~ s/([^a-zA-Z0-9_.-])/uc sprintf "%%%02x", ord $1 /eg;
  84         494  
172             }
173 245         666 return $toencode;
174             }
175              
176             # unescape URL-encoded data
177             sub unescape {
178 118     118 0 15519 my ( $todecode ) = @_;
179 118 50       227 return undef unless defined $todecode;
180 118         204 $todecode =~ tr/+/ /;
181 118 50       238 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         363 $todecode =~ s/%(?:([0-9a-fA-F]{2})|u([0-9a-fA-F]{4}))/
186 45 50       231 defined($1)? chr hex($1) : utf8_chr(hex($2))/ge;
187             }
188 118         326 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   222 $UTIL = CGI::Simple::Util->new; # initialize our $UTIL object
242              
243             sub new {
244 21     21 0 53 my $class = shift;
245 21   33     164 $class = ref( $class ) || $class;
246 21         51 my $self = {};
247 21         60 bless $self, $class;
248 21         115 $self->init;
249 21         780 return $self;
250             }
251              
252             sub init {
253 21     21 0 35 my $self = shift;
254 21         61 $self->charset;
255 21         63 $self->os;
256 21         59 $self->ebcdic;
257             }
258              
259             sub charset {
260 66     66 0 151 my ( $self, $charset ) = @_;
261 66 100       233 $self->{'charset'} = $charset if $charset;
262 66   100     443 $self->{'charset'}
263             ||= 'ISO-8859-1'; # set to the safe ISO-8859-1 if not defined
264 66         155 return $self->{'charset'};
265             }
266              
267             sub os {
268 21     21 0 61 my ( $self, $OS ) = @_;
269 21 50       80 $self->{'os'} = $OS if $OS; # allow value to be set manually
270 21         49 $OS = $self->{'os'};
271 21 50       60 unless ( $OS ) {
272 21 50       115 unless ( $OS = $^O ) {
273 0         0 require Config;
274 0         0 $OS = $Config::Config{'osname'};
275             }
276 21 50       352 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         45 $OS = 'UNIX';
296             }
297             }
298 21         69 return $self->{'os'} = $OS;
299             }
300              
301             sub ebcdic {
302 21     21 0 38 my $self = shift;
303 21 50       65 return $self->{'ebcdic'} if exists $self->{'ebcdic'};
304 21         58 $self->{'ebcdic'} = "\t" ne "\011" ? 1 : 0;
305 21 50       63 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__