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   147628 use strict;
  21         72  
  21         704  
3 21     21   110 use warnings;
  21         41  
  21         835  
4 21     21   120 use vars qw( $VERSION @EXPORT_OK @ISA $UTIL );
  21         43  
  21         59486  
5             $VERSION = '1.26';
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 565 my ( $order, @params ) = @_;
15 216         412 my ( %pos, @result, %leftover );
16 216 100       580 return () unless @params;
17 185 50       440 if ( ref $params[0] eq 'HASH' ) {
18 0         0 @params = %{ $params[0] };
  0         0  
19             }
20             else {
21 185 100       849 return @params unless $params[0] =~ m/^-/;
22             }
23              
24             # map parameters into positional indices
25 162         252 my $i = 0;
26 162         322 for ( @$order ) {
27 1215 100       2115 for ( ref( $_ ) eq 'ARRAY' ? @$_ : $_ ) { $pos{ lc( $_ ) } = $i; }
  1541         2680  
28 1215         1592 $i++;
29             }
30 162         463 $#result = $#$order; # preextend
31 162         391 while ( @params ) {
32 477         780 my $key = lc( shift( @params ) );
33 477         1264 $key =~ s/^\-//;
34 477 100       934 if ( exists $pos{$key} ) {
35 451         1147 $result[ $pos{$key} ] = shift( @params );
36             }
37             else {
38 26         81 $leftover{$key} = shift( @params );
39             }
40             }
41 162 100       389 push @result, make_attributes( \%leftover, 1 ) if %leftover;
42 162         917 return @result;
43             }
44              
45             sub make_attributes {
46 25     25 0 44 my $attref = shift;
47 25   50     55 my $escape = shift || 0;
48 25 50 33     117 return () unless $attref && ref $attref eq 'HASH';
49 25         36 my @attrib;
50 25         46 for my $key ( keys %{$attref} ) {
  25         75  
51 26         53 ( my $mod_key = $key ) =~ s/^-//; # get rid of initial - if present
52 26         50 $mod_key = lc $mod_key; # parameters are lower case
53 26         55 $mod_key =~ tr/_/-/; # use dashes
54             my $value
55 26 50       136 = $escape ? escapeHTML( $attref->{$key} ) : $attref->{$key};
56 26 50       119 push @attrib, defined $value ? qq/$mod_key="$value"/ : $mod_key;
57             }
58 25         70 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 85 my ( $time, $format ) = @_;
66 43   50     88 $format ||= 'http';
67 43         136 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         85 $time = _expire_calc( $time );
72 43 100       216 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       81 my $sc = $format eq 'cookie' ? '-' : ' ';
77 32         215 my ( $sec, $min, $hour, $mday, $mon, $year, $wday ) = gmtime( $time );
78 32         74 $year += 1900;
79 32         263 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   77 my ( $time ) = @_;
98 48         190 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         77 my $offset;
107 48 100 100     246 if ( !$time or lc $time eq 'now' ) {
    100          
    100          
108 27         44 $offset = 0;
109             }
110             elsif ( $time =~ /^\d+/ ) {
111 1         5 return $time;
112             }
113             elsif ( $time =~ /^([+-]?(?:\d+|\d*\.\d*))([mhdMy]?)/ ) {
114 9   50     44 $offset = ( $mult{$2} || 1 ) * $1;
115             }
116             else {
117 11         42 return $time;
118             }
119 36         71 my $cur_time = time;
120 36         119 return ( $cur_time + $offset );
121             }
122              
123             sub escapeHTML {
124 44     44 0 145 my ( $escape, $text ) = @_;
125 44 100       118 return undef unless defined $escape;
126 42         124 $escape =~ s/&/&/g;
127 42         78 $escape =~ s/"/"/g;
128 42         69 $escape =~ s/
129 42         199 $escape =~ s/>/>/g;
130              
131             # these next optional escapes make text look the same when rendered in HTML
132 42 50       110 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         104 return $escape;
138             }
139              
140             sub unescapeHTML {
141 135     135 0 232 my ( $unescape ) = @_;
142 135 100       302 return undef unless defined( $unescape );
143 122         358 my $latin = $UTIL->{'charset'} =~ /^(?:ISO-8859-1|WINDOWS-1252)$/i;
144 122         170 my $ebcdic = $UTIL->{'ebcdic'};
145              
146             # credit to Randal Schwartz for original version of this
147 122         206 $unescape =~ s[&(.*?);]{
148 27         63 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     187 /^#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         331 return $unescape;
160             }
161              
162             # URL-encode data
163             sub escape {
164 245     245 0 15500 my ( $toencode ) = @_;
165 245 50       442 return undef unless defined $toencode;
166 245 50       464 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         671 $toencode =~ s/([^a-zA-Z0-9_.-])/uc sprintf "%%%02x", ord $1 /eg;
  84         512  
172             }
173 245         686 return $toencode;
174             }
175              
176             # unescape URL-encoded data
177             sub unescape {
178 118     118 0 16029 my ( $todecode ) = @_;
179 118 50       224 return undef unless defined $todecode;
180 118         198 $todecode =~ tr/+/ /;
181 118 50       249 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         361 $todecode =~ s/%(?:([0-9a-fA-F]{2})|u([0-9a-fA-F]{4}))/
186 45 50       249 defined($1)? chr hex($1) : utf8_chr(hex($2))/ge;
187             }
188 118         318 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   245 $UTIL = CGI::Simple::Util->new; # initialize our $UTIL object
242              
243             sub new {
244 21     21 0 62 my $class = shift;
245 21   33     165 $class = ref( $class ) || $class;
246 21         55 my $self = {};
247 21         66 bless $self, $class;
248 21         82 $self->init;
249 21         823 return $self;
250             }
251              
252             sub init {
253 21     21 0 41 my $self = shift;
254 21         55 $self->charset;
255 21         69 $self->os;
256 21         65 $self->ebcdic;
257             }
258              
259             sub charset {
260 66     66 0 166 my ( $self, $charset ) = @_;
261 66 100       244 $self->{'charset'} = $charset if $charset;
262 66   100     398 $self->{'charset'}
263             ||= 'ISO-8859-1'; # set to the safe ISO-8859-1 if not defined
264 66         172 return $self->{'charset'};
265             }
266              
267             sub os {
268 21     21 0 43 my ( $self, $OS ) = @_;
269 21 50       69 $self->{'os'} = $OS if $OS; # allow value to be set manually
270 21         65 $OS = $self->{'os'};
271 21 50       76 unless ( $OS ) {
272 21 50       121 unless ( $OS = $^O ) {
273 0         0 require Config;
274 0         0 $OS = $Config::Config{'osname'};
275             }
276 21 50       311 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         52 $OS = 'UNIX';
296             }
297             }
298 21         73 return $self->{'os'} = $OS;
299             }
300              
301             sub ebcdic {
302 21     21 0 45 my $self = shift;
303 21 50       59 return $self->{'ebcdic'} if exists $self->{'ebcdic'};
304 21         79 $self->{'ebcdic'} = "\t" ne "\011" ? 1 : 0;
305 21 50       101 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__