blib/lib/PLP/Functions.pm | |||
---|---|---|---|
Criterion | Covered | Total | % |
statement | 71 | 122 | 58.2 |
branch | 23 | 62 | 37.1 |
condition | 0 | 3 | 0.0 |
subroutine | 16 | 21 | 76.1 |
pod | 12 | 13 | 92.3 |
total | 122 | 221 | 55.2 |
line | stmt | bran | cond | sub | pod | time | code |
---|---|---|---|---|---|---|---|
1 | package PLP::Functions; | ||||||
2 | |||||||
3 | 3 | 3 | 83250 | use strict; | |||
3 | 7 | ||||||
3 | 715 | ||||||
4 | 3 | 3 | 19 | use warnings; | |||
3 | 5 | ||||||
3 | 101 | ||||||
5 | |||||||
6 | 3 | 3 | 17 | use base 'Exporter'; | |||
3 | 10 | ||||||
3 | 849 | ||||||
7 | 3 | 3 | 17 | use Carp; | |||
3 | 7 | ||||||
3 | 324 | ||||||
8 | 3 | 3 | 144 | use Fcntl qw(:flock); | |||
3 | 6 | ||||||
3 | 946 | ||||||
9 | |||||||
10 | our $VERSION = '1.01'; | ||||||
11 | our @EXPORT = qw/Entity DecodeURI EncodeURI Include include PLP_END | ||||||
12 | EscapeHTML | ||||||
13 | AddCookie ReadFile WriteFile AutoURL Counter exit/; | ||||||
14 | |||||||
15 | sub Include ($) { | ||||||
16 | 3 | 3 | 15 | no strict; | |||
3 | 7 | ||||||
3 | 12080 | ||||||
17 | 2 | 2 | 1 | 3 | $PLP::file = $_[0]; | ||
18 | 2 | 3 | $PLP::inA = 0; | ||||
19 | 2 | 3 | $PLP::inB = 0; | ||||
20 | 2 | 3 | local $@; | ||||
21 | 2 | 1 | 14 | eval 'package PLP::Script; no warnings; ' . PLP::source($PLP::file, 0, join ' ', (caller)[2,1]); | |||
1 | 1 | 6 | |||||
1 | 2 | ||||||
1 | 49 | ||||||
1 | 5 | ||||||
1 | 1 | ||||||
1 | 28 | ||||||
22 | 2 | 100 | 15 | if ($@) { | |||
23 | 1 | 50 | 4 | PLP::Functions::exit() if $@ =~ /\cS\cT\cO\cP/; | |||
24 | 1 | 3 | PLP::error($@, 1); | ||||
25 | } | ||||||
26 | } | ||||||
27 | |||||||
28 | sub include ($) { | ||||||
29 | 2 | 2 | 1 | 11 | goto &Include; | ||
30 | } | ||||||
31 | |||||||
32 | sub exit (;$) { | ||||||
33 | 1 | 1 | 0 | 12 | die "\cS\cT\cO\cP\n"; | ||
34 | } | ||||||
35 | |||||||
36 | sub PLP_END (&) { | ||||||
37 | 2 | 2 | 1 | 5 | push @PLP::END, shift; | ||
38 | } | ||||||
39 | |||||||
40 | sub EscapeHTML { | ||||||
41 | 5 | 100 | 5 | 1 | 267 | @_ == 1 or croak "Unsupported parameters given to EscapeHTML"; | |
42 | 4 | 100 | 18 | unshift @_, shift if defined wantarray; # dereference if not void | |||
43 | 4 | 9 | for ($_[0]) { | ||||
44 | 4 | 100 | 13 | defined or next; | |||
45 | 3 | 22 | s/&/&/g; | ||||
46 | 2 | 9 | s/"/"/g; | ||||
47 | 2 | 8 | s/</g; | ||||
48 | 2 | 11 | s/>/>/g; | ||||
49 | } | ||||||
50 | 3 | 17 | return $_[0]; | ||||
51 | } | ||||||
52 | |||||||
53 | sub Entity (@) { | ||||||
54 | 2 | 50 | 2 | 1 | 21 | my $ref = defined wantarray ? [@_] : \@_; | |
55 | 2 | 7 | for (@$ref) { | ||||
56 | 2 | 50 | 8 | defined or next; | |||
57 | 2 | 4 | eval { | ||||
58 | 2 | 10 | s/&/&/g; | ||||
59 | 2 | 7 | s/"/"/g; | ||||
60 | 2 | 6 | s/</g; | ||||
61 | 2 | 4 | s/>/>/g; | ||||
62 | 2 | 8 | s/\n/ \n/g; |
||||
63 | 2 | 6 | s/\t/ /g; | ||||
64 | 2 | 8 | s/ / /g; | ||||
65 | }; | ||||||
66 | } | ||||||
67 | 2 | 50 | 23 | return defined wantarray ? (wantarray ? @$ref : "@$ref") : undef; | |||
50 | |||||||
68 | } | ||||||
69 | |||||||
70 | sub DecodeURI (@) { | ||||||
71 | 38 | 100 | 38 | 1 | 87 | my $ref = defined wantarray ? [@_] : \@_; | |
72 | 38 | 63 | for (@$ref) { | ||||
73 | 74 | 50 | 126 | defined or next; | |||
74 | 74 | 116 | eval { | ||||
75 | 74 | 86 | tr/+/ /; # Browsers do tr/ /+/ - I don't care about RFCs, but | ||||
76 | # I do care about real-life situations. | ||||||
77 | 74 | 155 | s/%([0-9A-Fa-f][0-9A-Fa-f])/chr hex $1/ge; | ||||
7 | 29 | ||||||
78 | }; | ||||||
79 | } | ||||||
80 | 38 | 50 | 135 | return defined wantarray ? (wantarray ? @$ref : "@$ref") : undef; | |||
100 | |||||||
81 | } | ||||||
82 | |||||||
83 | sub EncodeURI (@) { | ||||||
84 | 1 | 50 | 1 | 1 | 7 | my $ref = defined wantarray ? [@_] : \@_; | |
85 | 1 | 3 | for (@$ref) { | ||||
86 | 1 | 50 | 5 | defined or next; | |||
87 | 1 | 1 | eval { | ||||
88 | 1 | 7 | s{([^A-Za-z0-9\-_.!~*'()/?:@\$,])}{sprintf("%%%02x", ord $1)}ge; | ||||
4 | 20 | ||||||
89 | }; | ||||||
90 | } | ||||||
91 | 1 | 50 | 11 | return defined wantarray ? (wantarray ? @$ref : "@$ref") : undef; | |||
50 | |||||||
92 | } | ||||||
93 | |||||||
94 | sub AddCookie ($) { | ||||||
95 | 0 | 0 | 0 | 1 | 0 | if ($PLP::Script::header{'Set-Cookie'}) { | |
96 | 0 | 0 | $PLP::Script::header{'Set-Cookie'} .= "\n" . $_[0]; | ||||
97 | } else { | ||||||
98 | 0 | 0 | $PLP::Script::header{'Set-Cookie'} = $_[0]; | ||||
99 | } | ||||||
100 | } | ||||||
101 | |||||||
102 | sub ReadFile ($) { | ||||||
103 | 0 | 0 | 1 | 0 | local $/ = undef; | ||
104 | 0 | 0 | 0 | open (my $fh, '<', $_[0]) or do { | |||
105 | 0 | 0 | PLP::error("Cannot open $_[0] for reading ($!)", 1); | ||||
106 | 0 | 0 | return undef; | ||||
107 | }; | ||||||
108 | 0 | 0 | my $r = readline $fh; | ||||
109 | 0 | 0 | close $fh; | ||||
110 | 0 | 0 | return $r; | ||||
111 | } | ||||||
112 | |||||||
113 | sub WriteFile ($$) { | ||||||
114 | 0 | 0 | 0 | 1 | 0 | open (my $fh, '>', $_[0]) or do { | |
115 | 0 | 0 | PLP::error("Cannot open $_[0] for writing ($!)", 1); | ||||
116 | 0 | 0 | return undef; | ||||
117 | }; | ||||||
118 | 0 | 0 | flock $fh, LOCK_EX; | ||||
119 | 0 | 0 | 0 | print $fh $_[1] or do { | |||
120 | 0 | 0 | PLP::error("Cannot write to $_[0] ($!)"); | ||||
121 | 0 | 0 | return undef; | ||||
122 | }; | ||||||
123 | 0 | 0 | 0 | close $fh or do { | |||
124 | 0 | 0 | PLP::error("Cannot close $_[0] ($!)"); | ||||
125 | 0 | 0 | return undef; | ||||
126 | }; | ||||||
127 | 0 | 0 | return 1; | ||||
128 | } | ||||||
129 | |||||||
130 | sub Counter ($) { | ||||||
131 | 0 | 0 | 1 | 0 | local $/ = undef; | ||
132 | 0 | 0 | my $fh; | ||||
133 | 0 | 0 | 0 | 0 | open $fh, '+<', $_[0] or | ||
134 | open $fh, '>', $_[0] or return undef; | ||||||
135 | 0 | 0 | flock $fh, 2; | ||||
136 | 0 | 0 | seek $fh, 0, 0; | ||||
137 | 0 | 0 | my $counter = <$fh>; | ||||
138 | 0 | 0 | seek $fh, 0, 0; | ||||
139 | 0 | 0 | truncate $fh, 0; | ||||
140 | 0 | 0 | 0 | print $fh ++$counter or return undef; | |||
141 | 0 | 0 | 0 | close $fh or return undef; | |||
142 | 0 | 0 | return $counter; | ||||
143 | } | ||||||
144 | |||||||
145 | sub AutoURL ($) { | ||||||
146 | # This sub assumes your string does not match /(["<>])\cC\1/ | ||||||
147 | 0 | 0 | 0 | 1 | 0 | my $ref = defined wantarray ? \(my $copy = $_[0]) : \$_[0]; | |
148 | 0 | 0 | eval { | ||||
149 | 0 | 0 | $$ref =~ s/"/"\cC"/g; # Single characters are easier to match :) | ||||
150 | 0 | 0 | $$ref =~ s/>/>\cC>/g; # so we can just use a character class [] | ||||
151 | 0 | 0 | $$ref =~ s/</<\cC | ||||
152 | |||||||
153 | # Now this is a big, ugly regex! But hey - it works :) | ||||||
154 | 0 | 0 | $$ref =~ s{((\w+://|www\.|WWW\.)[a-zA-Z0-9\.\@:-]+[^\"\'>< \r\t\n]*)}{ | ||||
155 | 0 | 0 | local $_ = $1; | ||||
156 | 0 | 0 | my $scheme = $2; | ||||
157 | 0 | 0 | 0 | s/// if (my $trailing) = /([\.,!\?\(\)\[\]]+$)/; | |||
158 | 0 | 0 | s/&(?!\x23?\w+;)/&/g; | ||||
159 | 0 | 0 | s/\"/"/g; | ||||
160 | 0 | 0 | 0 | my $href = ($scheme =~ /www\./i ? "http://$_" : $_); | |||
161 | 0 | 0 | qq{$_$trailing}; | ||||
162 | }eg; | ||||||
163 | |||||||
164 | 0 | 0 | $$ref =~ s/"\cC"/"/g; | ||||
165 | 0 | 0 | $$ref =~ s/>\cC>/>/g; | ||||
166 | 0 | 0 | $$ref =~ s/<\cC</g; | ||||
167 | }; | ||||||
168 | 0 | 0 | 0 | if ($@){ return defined wantarray ? @_ : undef } # return original on error | |||
0 | 0 | 0 | |||||
169 | 0 | 0 | 0 | return defined wantarray ? $$ref : undef; | |||
170 | } | ||||||
171 | |||||||
172 | 1; | ||||||
173 | |||||||
174 | =head1 NAME | ||||||
175 | |||||||
176 | PLP::Functions - Functions that are available in PLP documents | ||||||
177 | |||||||
178 | =head1 DESCRIPTION | ||||||
179 | |||||||
180 | The functions are exported into the PLP::Script package that is used by PLP documents. Although uppercased letters are unusual in Perl, they were chosen to stand out. | ||||||
181 | |||||||
182 | Most of these functions are context-hybird. Before using them, one should know about contexts in Perl. The three major contexts are: B
|
||||||
183 | |||||||
184 | Some context examples: | ||||||
185 | |||||||
186 | print foo(); # foo is in list context (print LIST) | ||||||
187 | foo(); # foo is in void context | ||||||
188 | $bar = foo(); # foo is in scalar context | ||||||
189 | @bar = foo(); # foo is in list context | ||||||
190 | length foo(); # foo is in scalar context (length EXPR) | ||||||
191 | |||||||
192 | =head2 The functions | ||||||
193 | |||||||
194 | =over 10 | ||||||
195 | |||||||
196 | =item Include FILENAME | ||||||
197 | |||||||
198 | Executes another PLP file, that will be parsed (i.e. code must be in C<< <: :> >>). As with Perl's C |
||||||
199 | |||||||
200 | Include can be used recursively, and there is no depth limit: | ||||||
201 | |||||||
202 | |||||||
203 | <: | ||||||
204 | include 'crash.plp'; | ||||||
205 | # This example will loop forever, | ||||||
206 | # and dies with an out of memory error. | ||||||
207 | # Do not try this at home. | ||||||
208 | :> | ||||||
209 | |||||||
210 | =item include FILENAME | ||||||
211 | |||||||
212 | An alias for C |
||||||
213 | |||||||
214 | =item PLP_END BLOCK | ||||||
215 | |||||||
216 | Adds a piece of code that is executed when at the end of the PLP document. This is useful when creating a template file: | ||||||
217 | |||||||
218 | |||||||
219 | <: PLP_END { :> | ||||||
220 | |||||||
221 | <: } :> | ||||||
222 | |||||||
223 | <(template.plp)> | ||||||
224 | Hello, world! | ||||||
225 | |||||||
226 | You should use this function instead of Perl's built-in C |
||||||
227 | |||||||
228 | =item EscapeHTML STRING | ||||||
229 | |||||||
230 | Replaces HTML syntax characters by HTML entities, so the text can be output safely. | ||||||
231 | You should always use this when displaying user input (or database output), | ||||||
232 | to avoid cross-site-scripting vurnerabilities. | ||||||
233 | |||||||
234 | In void context, B |
||||||
235 | |||||||
236 | <: EscapeHTML($user_input); print "$user_input"; :> |
||||||
237 | |||||||
238 | In other contexts, returns the changed version. | ||||||
239 | |||||||
240 | |||||||
241 | |||||||
242 | Be warned that single quotes are not substituted, so always use double quotes for attributes. | ||||||
243 | Also does not convert whitespace for formatted output; use Entity() for that. | ||||||
244 | |||||||
245 | To escape high-bit characters as well, refer to L |
||||||
246 | |||||||
247 | =item Entity LIST | ||||||
248 | |||||||
249 | Formats given arguments for literal display in HTML documents. | ||||||
250 | Similar to EscapeHTML(), but also preserves newlines and consecutive spaces | ||||||
251 | using corresponding C<< >> and C< > respectively. |
||||||
252 | |||||||
253 | In void context, B |
||||||
254 | |||||||
255 | <: print ' ' . Entity($user_input) . ' '; :> |
||||||
256 | |||||||
257 | Inside attributes, always use EscapeHTML() instead. | ||||||
258 | |||||||
259 | =item EncodeURI LIST | ||||||
260 | |||||||
261 | Encodes URI strings according to RFC 3986. All disallowed characters are replaced by their %-encoded values. | ||||||
262 | |||||||
263 | In void context, B |
||||||
264 | |||||||
265 | Link | ||||||
266 | |||||||
267 | Note that the following reserved characters are I |
||||||
268 | |||||||
269 | / ? : @ $ | ||||||
270 | |||||||
271 | This should be safe for escaping query values (as in the example above), | ||||||
272 | but otherwise it may be a better idea to use L |
||||||
273 | |||||||
274 | =item DecodeURI LIST | ||||||
275 | |||||||
276 | Decodes %-encoded strings. Unlike L |
||||||
277 | it also translates + characters to spaces (as browsers use those). | ||||||
278 | |||||||
279 | In void context, B |
||||||
280 | |||||||
281 | =item ReadFile FILENAME | ||||||
282 | |||||||
283 | Returns the contents of FILENAME in one large string. Returns undef on failure. | ||||||
284 | |||||||
285 | =item WriteFile FILENAME, STRING | ||||||
286 | |||||||
287 | Writes STRING to FILENAME (overwrites FILENAME if it already exists). Returns true on success, false on failure. | ||||||
288 | |||||||
289 | =item Counter FILENAME | ||||||
290 | |||||||
291 | Increases the contents of FILENAME by one and returns the new value. Returns undef on failure. Fails silently. | ||||||
292 | |||||||
293 | You are visitor number <:= Counter('counter.txt') :>. | ||||||
294 | |||||||
295 | =item AutoURL STRING | ||||||
296 | |||||||
297 | Replaces URLs (actually, replace things that look like URLs) by links. | ||||||
298 | |||||||
299 | In void context, B |
||||||
300 | |||||||
301 | <: print AutoURL(Entity($user_input)); :> | ||||||
302 | |||||||
303 | =item AddCookie STRING | ||||||
304 | |||||||
305 | Adds a Set-Cookie header. STRING must be a valid Set-Cookie header value. | ||||||
306 | |||||||
307 | =back | ||||||
308 | |||||||
309 | =head1 AUTHOR | ||||||
310 | |||||||
311 | Juerd Waalboer |
||||||
312 | |||||||
313 | Current maintainer: Mischa POSLAWSKY |
||||||
314 | |||||||
315 | =cut | ||||||
316 |