File Coverage

blib/lib/YATT/Lite/Util.pm
Criterion Covered Total %
statement 483 626 77.1
branch 163 282 57.8
condition 82 148 55.4
subroutine 113 132 85.6
pod 0 69 0.0
total 841 1257 66.9


line stmt bran cond sub pod time code
1             package YATT::Lite::Util;
2 40     40   105850 use strict;
  40         88  
  40         1080  
3 40     40   222 use warnings qw(FATAL all NONFATAL misc);
  40         82  
  40         1472  
4 40     40   508 use constant DEBUG_LOOKUP_PATH => $ENV{DEBUG_YATT_UTIL_LOOKUP_PATH};
  40         88  
  40         2097  
5              
6 37     37   926 use Encode ();
  37         248930  
  37         789  
7              
8 37     37   1169 use URI::Escape ();
  37         55895  
  37         857  
9 37     37   1283 use Tie::IxHash;
  37         126224  
  37         1516  
10              
11             require Scalar::Util;
12              
13             {
14             package YATT::Lite::Util;
15 37     37   256 use Exporter qw(import);
  37         77  
  37         2048  
16             BEGIN {
17 37     37   147 $INC{'YATT/Lite/Util.pm'} = 1;
18 37         611 our @EXPORT = qw/numLines coalesce default globref symtab lexpand escape
19             untaint_any ckeval ckrequire untaint_unless_tainted
20             dict_sort terse_dump catch
21             nonempty
22             empty
23             subname
24             pkg2pm
25             globref_default
26             /;
27             }
28 37     37   218 use Carp;
  37         181  
  37         8063  
29             sub numLines {
30 4683 50   4683 0 11565 croak "undefined value for numLines!" unless defined $_[0];
31 4683         11891 $_[0] =~ tr|\n||;
32             }
33             sub coalesce {
34 1754     1754 0 3479 foreach my $item (@_) {
35 3488 100       10845 return $item if defined $item;
36             }
37 0         0 undef;
38             }
39             *default = \*coalesce;
40              
41             sub nonempty {
42 369 100   369 0 2843 defined $_[0] && $_[0] ne '';
43             }
44             sub empty {
45 576 50   576 0 3007 not defined $_[0] or $_[0] eq '';
46             }
47              
48             sub define_const {
49 1986     1986 0 6313 my ($name_or_glob, $value) = @_;
50 1986 100       4705 my $glob = ref $name_or_glob ? $name_or_glob : globref($name_or_glob);
51 1986     0   11982 *$glob = my $const_sub = sub () { $value };
  0         0  
52 1986         5551 $const_sub;
53             }
54              
55             sub globref {
56 31246     31246 0 62285 my ($thing, @name) = @_;
57 31246   66     84848 my $class = ref $thing || $thing;
58 37     37   343 no strict 'refs';
  37         136  
  37         24803  
59 31246         43817 \*{join("::", $class, grep {defined} @name)};
  31246         51062  
  31152         202120  
60             }
61             sub globref_default {
62 154 50   154 0 667 unless (@_ == 2) {
63 0         0 croak "Too few arguments";
64             }
65 154         455 my ($globref, $default) = @_;
66 154         321 my $kind = ref $default;
67 154 100       242 *{$globref}{$kind} || do {
  154         1046  
68 146         231 *{$globref} = $default;
  146         323  
69 146         417 $default;
70             };
71             }
72             sub symtab {
73 13626         26828 *{globref(shift, '')}{HASH}
74 13626     13626 0 20501 }
75             # XXX: Nice to have look_for_symtab, too.
76             sub look_for_globref {
77 12289     12289 0 24105 my ($class, $name) = @_;
78 12289         24619 my $symtab = symtab($class);
79 12289 100       34698 return undef unless defined $symtab->{$name};
80 12192         25620 globref($class, $name);
81             }
82             sub fields_hash {
83 11137 100   11137 0 22479 my $sym = look_for_globref(shift, 'FIELDS')
84             or return undef;
85 11116         18085 *{$sym}{HASH};
  11116         26983  
86             }
87             # XXX: should be renamed to lhexpand
88             sub lexpand {
89             # lexpand can be used to counting.
90 3241 50   3241 0 79350 unless (defined $_[0]) {
    100          
    100          
    100          
91 1087 100       3941 wantarray ? () : 0;
92 0         0 } elsif (not ref $_[0]) {
93 881         2440 $_[0]
94 0         0 } elsif (ref $_[0] eq 'ARRAY') {
95 1271         2005 @{$_[0]}
  1271         4904  
96 0         0 } elsif (ref $_[0] eq 'HASH') {
97 2         3 %{$_[0]}
  2         23  
98             } else {
99 0 0       0 wantarray ? () : 0;
100             }
101             }
102             sub lsearch (&@) {
103 0     0 0 0 my $sub = shift;
104 0         0 my $i = 0;
105 0         0 foreach (@_) {
106 0 0       0 return $i if $sub->($_);
107 0         0 } continue {$i++}
108 0         0 return;
109             }
110             # $fn:e
111 0 0   0 0 0 sub extname { my $fn = shift; return $1 if $fn =~ s/\.(\w+)$// }
  0         0  
112             # $fn:r
113 1253     1253 0 3236 sub rootname { my $fn = shift; $fn =~ s/\.\w+$//; join "", $fn, @_ }
  1253         4591  
  1253         4709  
114             # $fn:r:t
115             sub appname {
116 38     38 0 45888 my $fn = shift;
117 38         280 $fn =~ s/\.\w+$//;
118 38 50       601 return $1 if $fn =~ m{(\w+)$};
119             }
120 554     554 0 1707 sub untaint_any { $_[0] =~ m{.*}s; $& }
  554         2601  
121             our $DEBUG_INJECT_TAINTED = 0;
122             # untaint_unless_tainted($fn, read_file($fn))
123             sub untaint_unless_tainted {
124 345 50   345 0 2897 return $_[1] unless ${^TAINT};
125 0 0 0     0 if (defined $_[0] and not Scalar::Util::tainted($_[0])) {
126 0 0       0 $DEBUG_INJECT_TAINTED ? $_[1] : untaint_any($_[1]);
127             } else {
128 0         0 $_[1];
129             }
130             }
131             sub ckeval {
132             my $__SCRIPT__ = join "", grep {
133 696 50   696 0 1646 defined $_ and Scalar::Util::tainted($_) ? croak "tainted! '$_'" : 1;
  3184 50       14294  
134             } @_;
135 696         1334 my @__RESULT__;
136 696 50       1852 if ($] < 5.014) {
137 0 0       0 if (wantarray) {
138 0         0 @__RESULT__ = eval $__SCRIPT__;
139             } else {
140 0         0 $__RESULT__[0] = eval $__SCRIPT__;
141             }
142 0 0       0 die $@ if $@;
143             } else {
144 696         1303 local $@;
145 696 50       1566 if (wantarray) {
146 0         0 @__RESULT__ = eval $__SCRIPT__;
147             } else {
148 24     24 0 162 $__RESULT__[0] = eval $__SCRIPT__;
  24     24 0 57  
  24     24 0 142  
  24     23   208  
  24     23   50  
  24     23   140  
  24     22   4027  
  24     21   64  
  24     21   169  
  23     20   152  
  23     20   47  
  23     20   149  
  23     19   234  
  23     18   53  
  23     18   133  
  23     17   1673  
  23     17   46  
  23     17   114  
  22     17   154  
  22     17   55  
  22     17   885  
  22     17   210  
  22     17   50  
  22     17   161  
  21     17   589  
  21     17   46  
  21     17   125  
  20     17   1148  
  20     17   45  
  20     17   198  
  20     17   131  
  20     17   48  
  20     17   179  
  20     17   366  
  20     14   51  
  20     14   97  
  19     13   1291  
  19     12   52  
  19     11   128  
  18     11   133  
  18     1   42  
  18     1   125  
  18         177  
  18         40  
  18         116  
  17         1586  
  17         38  
  17         101  
  17         128  
  17         39  
  17         92  
  17         99  
  17         38  
  17         91  
  17         1569  
  17         45  
  17         86  
  17         119  
  17         43  
  17         79  
  17         105  
  17         41  
  17         81  
  17         1409  
  17         40  
  17         91  
  17         114  
  17         40  
  17         79  
  17         93  
  17         38  
  17         67  
  17         1266  
  17         36  
  17         74  
  17         119  
  17         41  
  17         82  
  17         95  
  17         37  
  17         79  
  17         1264  
  17         47  
  17         79  
  17         108  
  17         39  
  17         77  
  17         98  
  17         39  
  17         84  
  17         1458  
  17         40  
  17         86  
  17         120  
  17         37  
  17         80  
  17         94  
  17         37  
  17         90  
  17         1327  
  17         38  
  17         91  
  14         96  
  14         34  
  14         150  
  14         78  
  14         36  
  14         125  
  13         1030  
  13         30  
  12         115  
  12         86  
  12         30  
  12         96  
  11         71  
  11         24  
  11         89  
  11         945  
  11         27  
  11         63  
  696         87692  
  4         12  
  4         16  
  4         26  
  1         7  
  1         5  
  1         3  
  1         4  
  1         1  
  1         3  
149             }
150 696 100       48385 die $@ if $@;
151             }
152 695 50       3076 wantarray ? @__RESULT__ : $__RESULT__[0];
153             }
154             sub ckrequire {
155 412     412 0 1385 ckeval("require $_[0]");
156             }
157 37     37   285 use Scalar::Util qw(refaddr);
  37         89  
  37         74190  
158             sub cached_in {
159 580     580 0 1771 my ($dir, $dict, $name, $sys, $mark, $loader, $refresher) = @_;
160 580 100       1792 if (not exists $dict->{$name}) {
161 139 100       679 my $item = $dict->{$name} = $loader ? $loader->($dir, $sys, $name)
162             : $dir->load($sys, $name);
163 139 100 100     900 $mark->{refaddr($item)} = 1 if $item and $mark;
164 139         586 $item;
165             } else {
166 441         1020 my $item = $dict->{$name};
167 441 100 100     4704 unless ($item and ref $item
    100 66        
168             and (not $mark or not $mark->{refaddr($item)}++)) {
169             # nop
170 0         0 } elsif ($refresher) {
171 34         153 $refresher->($item, $sys, $name)
172             } elsif (my $sub = UNIVERSAL::can($item, 'refresh')) {
173             $sub->($item, $sys);
174             }
175 441         1898 $item;
176             }
177             }
178              
179             sub split_path {
180 35     35 0 9669 my ($path, $startDir, $cut_depth, $default_ext) = @_;
181             # $startDir is $app_root.
182             # $doc_root should resides under $app_root.
183 35   50     99 $cut_depth //= 1;
184 35   100     138 $default_ext //= "yatt";
185 35         176 $startDir =~ s,/+$,,;
186 35 50       353 unless ($path =~ m{^\Q$startDir\E}gxs) {
187 0         0 die "Can't split_path: prefix mismatch: $startDir vs $path";
188             }
189              
190 35         120 my ($dir, $pos, $file) = ($startDir, pos($path));
191             # *DO NOT* initialize $file. This loop relies on undefined-ness of $file.
192 35   100     884 while ($path =~ m{\G/+([^/]*)}gcxs and -e "$dir/$1" and not defined $file) {
      66        
193 66 100       198 if (-d _) {
194 39         109 $dir .= "/$1";
195             } else {
196 27         68 $file = $1;
197             # *DO NOT* last. To match one more time.
198             }
199             } continue {
200 66         800 $pos = pos($path);
201             }
202              
203 35 50       132 $dir .= "/" if $dir !~ m{/$};
204 35         88 my $subpath = substr($path, $pos);
205 35 100       102 if (not defined $file) {
206 8 100 66     130 if ($subpath =~ m{^/(\w+)(?:/|$)} and -e "$dir/$1.$default_ext") {
    100          
    50          
207 6         20 $subpath = substr($subpath, 1+length $1);
208 6         13 $file = "$1.$default_ext";
209             } elsif (-e "$dir/index.$default_ext") {
210             # index.yatt should subsume all subpath.
211             } elsif ($subpath =~ s{^/([^/]+)$}{}) {
212             # Note: Actually, $file is not accesible in this case.
213             # This is just for better error diag.
214 1         4 $file = $1;
215             }
216             }
217              
218 35         90 my $loc = substr($dir, length($startDir));
219 35         109 while ($cut_depth-- > 0) {
220 33 50       170 $loc =~ s,^/[^/]+,,
221             or croak "Can't cut path location: $loc";
222 33         138 $startDir .= $&;
223             }
224              
225 35   100     295 ($startDir
226             , $loc
227             , $file // ''
228             , $subpath
229             , (not defined $file)
230             );
231             }
232              
233             sub lookup_dir {
234 261     261 0 693 my ($loc, $dirlist) = @_;
235 261         1011 $loc =~ s{^/*}{/};
236 261         753 foreach my $dir (@$dirlist) {
237 261         685 my $real = "$dir$loc";
238 261 50       3835 next unless -d $real;
239 261 50       1632 return wantarray ? ($real, $dir) : $real;
240             }
241             }
242              
243             sub lookup_path {
244 265     265 0 26738 my ($path_info, $dirlist, $index_name, $want_ext, $use_subpath) = @_;
245 265   100     909 $index_name //= 'index';
246 265   100     757 $want_ext //= '.yatt';
247 265         666 my $ixfn = $index_name . $want_ext;
248 265 50       658 my @dirlist = grep {defined $_ and -d $_} @$dirlist;
  301         6956  
249 265         615 print STDERR "dirlist" => terse_dump(@dirlist), "\n" if DEBUG_LOOKUP_PATH;
250 265         514 my $pi = $path_info;
251 265         714 my ($loc, $cur, $ext) = ("", "");
252             DIG:
253 265         2041 while ($pi =~ s{^/+([^/]+)}{}) {
254 320         959 $cur = $1;
255 320 100       1227 $ext = ($cur =~ s/(\.[^\.]+)$// ? $1 : undef);
256 320         535 print STDERR terse_dump(cur => $cur, ext => $ext), "\n" if DEBUG_LOOKUP_PATH;
257 320         845 foreach my $dir (@dirlist) {
258 348         984 my $base = "$dir$loc/$cur";
259 348 100 100     9099 if (defined $ext and -r "$base$ext") {
    100 100        
    100 100        
    100          
260             # If extension is specified and it is readable, use it.
261 31         316 return ($dir, "$loc/", "$cur$ext", $pi);
262             } elsif ($pi =~ m{^/} and -d $base) {
263             # path_info has '/' and directory exists.
264 81         299 next; # candidate
265             } elsif (-r (my $fn = "$base$want_ext")) {
266 112         1095 return ($dir, "$loc/", "$cur$want_ext", $pi);
267             } elsif ($use_subpath
268             and -r (my $alt = "$dir$loc/$ixfn")) {
269 91   50     562 $ext //= "";
270 91         995 return ($dir, "$loc/", $ixfn, "/$cur$ext$pi", 1);
271             } else {
272             # Neither dir nor $cur$want_ext exists, it should be ignored.
273 33         100 undef $dir;
274             }
275             }
276             } continue {
277 86         242 $loc .= "/$cur";
278 86         153 print STDERR terse_dump(continuing => $loc), "\n" if DEBUG_LOOKUP_PATH;
279 86         218 @dirlist = grep {defined} @dirlist;
  108         708  
280             }
281 31         58 print STDERR terse_dump('end_of_loop'), "\n" if DEBUG_LOOKUP_PATH;
282              
283 31 100       195 return unless $pi =~ m{^/+$};
284              
285 28         91 foreach my $dir (@dirlist) {
286 29 100       437 next unless -r "$dir$loc/$ixfn";
287 28         267 return ($dir, "$loc/", "$ixfn", "", 1);
288             }
289              
290 0         0 print STDERR terse_dump('at_last'), "\n" if DEBUG_LOOKUP_PATH;
291 0         0 return;
292             }
293              
294             sub trim_common_suffix_from {
295 10 50   10 0 1518 @_ == 2 or Carp::croak "trim_common_suffix_from(FROM, COMPARE)";
296 10         22 my $from = $_[0];
297 10         25 my $cmppos = (length $_[1]) - 1;
298 10   66     53 while (length $from and $cmppos >= 0) {
299 25 50       78 (my $slash = rindex($_[1], "/", $cmppos)) >= 0
300             or last;
301 25 100       77 (my $pos = rindex($from, substr($_[1], $slash, $cmppos - $slash))) >= 0
302             or last;
303              
304 20         47 substr($from, $pos) = "";
305              
306 20         67 $cmppos = $slash - 1;
307             }
308 10         45 $from;
309             }
310              
311             sub dict_order {
312 4     4 0 8 my ($a, $b, $start) = @_;
313 4 50       12 $start = 1 unless defined $start;
314 4         8 my ($result, $i) = (0);
315 4   33     18 for ($i = $start; $i <= $#$a and $i <= $#$b; $i++) {
316 32 100 66     103 if ($a->[$i] =~ /^\d/ and $b->[$i] =~ /^\d/) {
317 16         28 $result = $a->[$i] <=> $b->[$i];
318             } else {
319 16         25 $result = $a->[$i] cmp $b->[$i];
320             }
321 32 100       127 return $result unless $result == 0;
322             }
323 0         0 return $#$a <=> $#$b;
324             }
325              
326             # a => ['a', 'a']
327             # q1a => ['q1a', 'q', 1, 'a']
328             # q11b => ['q11b', 'q', 11, 'b']
329             sub dict_sort (@) {
330 2     2 0 369 map {$_->[0]} sort {dict_order($a,$b)} map {[$_, split /(\d+)/]} @_;
  5         13  
  4         7  
  5         43  
331             }
332             sub dict_sort_by_nth ($@) {
333 0     0 0 0 my $nth = shift;
334 0         0 map {$_->[0]}
335 0         0 sort {dict_order($a,$b)}
336 0         0 map {[$_, split /(\d+)/, $$_[$nth]]} @_;
  0         0  
337             }
338              
339             sub combination (@) {
340 2     2 0 9012 my $comb; $comb = sub {
341 6     6   20 my $prefix = shift;
342 6 50       29 return $prefix unless @_;
343 6         18 my ($list, @rest) = @_;
344 6 100       28 if (@rest) {
345 2         10 map {$comb->([@$prefix, $_], @rest)} @$list;
  4         23  
346             } else {
347 4         20 map {[@$prefix, $_]} @$list;
  8         38  
348             }
349 2         38 };
350 2         24 $comb->([], @_);
351             }
352              
353             sub captured (&) {
354 5     5 0 703 my ($code) = @_;
355 5 50       74 open my $fh, '>:utf8', \ (my $buffer = "") or die "Can't create capture buf:$!";
356 5         671 $code->($fh);
357 4         40 $buffer;
358             }
359              
360             sub terse_dump {
361 160     160 0 28838 require Data::Dumper;
362             join ", ", map {
363 160         16537 Data::Dumper->new([$_])->Terse(1)->Indent(0)->Dump;
  222         3840  
364             } @_;
365             }
366              
367             sub is_debugging {
368 103 50   103 0 597 my $symtab = $main::{'DB::'} or return 0;
369 103         214 defined ${*{$symtab}{HASH}}{cmd_b}
  103         1026  
370 103         189 }
371              
372             sub catch (&) {
373 405     405 0 3183 my ($sub) = @_;
374 405         944 local $@ = '';
375 405         889 eval { $sub->() };
  405         1166  
376 405         14248 $@;
377             }
378             }
379              
380             sub dofile_in {
381 8     8 0 21 my ($pkg, $file) = @_;
382 8 50 33     106 unless (-e $file) {
383 0         0 croak "No such file: $file\n";
384             } elsif (not -r _) {
385             croak "Can't read file: $file\n";
386             }
387 8         54 ckeval("package $pkg; my \$result = do '$file'; die \$\@ if \$\@; \$result");
388             }
389              
390             sub compile_file_in {
391 3     3 0 11 my ($pkg, $file) = @_;
392 3 50       39 if (-d $file) {
393 0         0 croak "file '$file' is a directory!";
394             }
395 3         14 my $sub = dofile_in($pkg, $file);
396 3 50 33     29 unless (defined $sub and ref $sub eq 'CODE') {
397 0         0 die "file '$file' should return CODE (but not)!\n";
398             }
399 3         13 $sub;
400             }
401              
402              
403 1         2 BEGIN {
404 37     37   331 my %escape = (qw(< <
405             > >
406             --> -->
407             " "
408             & &)
409             , "\'", "'");
410              
411 37         2511 our $ESCAPE_UNDEF = '';
412              
413             sub escape {
414 315 100 100 315 0 10212 return if wantarray && !@_;
415 314         574 my @result;
416 314         668 foreach my $str (@_) {
417 314         528 push @result, do {
418 314 0       1016 unless (defined $str) {
    50          
    100          
    100          
    100          
419 5         13 $ESCAPE_UNDEF;
420 0         0 } elsif (not ref $str) {
421 297         588 my $copy = $str;
422 297         819 $copy =~ s{([<>&\"\'])}{$escape{$1}}g;
423 297         793 $copy;
424 0         0 } elsif (ref $str eq 'SCALAR') {
425             # PASS Thru. (Already escaped)
426 5   33     30 $$str // $ESCAPE_UNDEF; # fail safe
427 0         0 } elsif (_is_escapable($str)) {
428 7         19 $str->as_escaped;
429 0         0 } elsif (my $sub = UNIVERSAL::can($str, 'cf_pairs')) {
430             ref($str).'->new('.(join(", ", map {
431 0         0 my ($k, $v) = @$_;
  0         0  
432 0         0 "$k => " . do {
433 0         0 my $esc = escape($v);
434 0 0       0 if (not defined $esc) {
    0          
435 0         0 'undef'
436             } elsif ($esc eq '') {
437 0         0 "''"
438             } else {
439 0         0 $esc;
440             }
441             };
442             } $sub->($str))).')';
443             } else {
444             # XXX: Is this secure???
445             # XXX: Should be JSON?
446 0         0 my $copy = terse_dump($str);
447 0         0 $copy =~ s{([<\"]|-->)}{$escape{$1}}g; # XXX: Minimum. May be insecure.
448 0         0 $copy;
449             }
450             };
451             }
452 314 100       1926 wantarray ? @result : $result[0];
453             }
454             }
455              
456             # XXX: Since method name "as_escaped" conflicts with CGen::Perl->as_escaped,
457             # We need a informational class for everything safely escapable
458             # via "as_escape()"
459             {
460             sub _is_escapable {
461 7     7   28 UNIVERSAL::isa($_[0], 'YATT::Lite::Util::escapable');
462             }
463             package
464             YATT::Lite::Util::escapable;
465             }
466              
467             {
468             package
469             YATT::Lite::Util::named_attr;
470 37     37   1262 BEGIN {our @ISA = ('YATT::Lite::Util::escapable')};
471 37     37   527 use overload qw("" as_string);
  37         15874  
  37         306  
472             sub as_string {
473 0     0   0 shift->[-1];
474             }
475             sub as_escaped {
476 7     7   59 sprintf q{ %s="%s"}, $_[0][0], $_[0][1];
477             }
478             }
479              
480             sub named_attr {
481 8     8 0 335 my $attname = shift;
482 8 100       17 my @result = grep {defined $_ && $_ ne ''} @_;
  12         59  
483 8 100       22 return '' unless @result;
484 7         13 bless [$attname, join ' ', map {escape($_)} @result]
  10         23  
485             , 'YATT::Lite::Util::named_attr';
486             }
487              
488             {
489             # XXX: These functions are deprecated. Use att_value_in() instead.
490              
491 0     0 0 0 sub value_checked { _value_checked($_[0], $_[1], checked => '') }
492 0     0 0 0 sub value_selected { _value_checked($_[0], $_[1], selected => '') }
493              
494             sub _value_checked {
495 0     0   0 my ($value, $hash, $then, $else) = @_;
496 0         0 sprintf q|value="%s"%s|, escape($value)
497             , _if_checked($hash, $value, $then, $else);
498             }
499              
500             sub _if_checked {
501 0     0   0 my ($in, $value, $then, $else) = @_;
502 0   0     0 $else //= '';
503 0 0       0 return $else unless defined $in;
504 0 0 0     0 if (ref $in ? $in->{$value // ''} : ($in eq $value)) {
    0          
505 0         0 " $then"
506             } else {
507 0         0 $else;
508             }
509             }
510             }
511              
512             {
513             our %input_spec = (select => [0, 0]
514             , radio => [1, 0]
515             , checkbox => [2, 1]);
516             sub att_value_in {
517 0     0 0 0 my ($in, $type, $name, $formal_value, $as_value, $is_default) = @_;
518 0 0       0 defined (my $spec = $input_spec{$type})
519             or croak "Unknown type: $type";
520              
521 0         0 my ($typeid, $has_sfx) = @$spec;
522              
523 0 0 0     0 unless (defined $name and $name ne '') {
524 0         0 croak "name is empty";
525             }
526              
527 0 0 0     0 unless (defined $formal_value and $formal_value ne '') {
528 0         0 croak "value is empty";
529             }
530              
531 0         0 my @res;
532              
533 0 0 0     0 if ($type and $typeid) {
534 0         0 push @res, qq|type="$type"|;
535             }
536              
537 0 0       0 if ($typeid) {
538 0 0       0 my $sfx = $has_sfx ? '['.escape($formal_value).']' : '';
539 0         0 push @res, qq|name="@{[escape($name)]}$sfx"|;
  0         0  
540             }
541              
542 0 0       0 if (not $has_sfx) {
    0          
543             # select
544 0         0 push @res, qq|value="@{[escape($formal_value)]}"|;
  0         0  
545             } elsif ($as_value) {
546             # checkbox/radio, with explicit value
547 0         0 push @res, qq|value="@{[escape($as_value)]}"|;
  0         0  
548             }
549              
550 0 0       0 if (find_value_in($in, $name, $formal_value, $is_default)) {
551 0 0       0 push @res, $typeid ? "checked" : "selected";
552             }
553              
554 0         0 join(" ", @res);
555             }
556              
557             sub find_value_in {
558 0     0 0 0 my ($in, $name, $formal_value, $is_default) = @_;
559              
560 0         0 my $actual_value = do {
561 0 0       0 if (my $sub = $in->can("param")) {
    0          
562 0         0 $sub->($in, $name);
563             } elsif (ref $in eq 'HASH') {
564 0         0 $in->{$name};
565             } else {
566 0         0 undef;
567             }
568             };
569              
570 0 0       0 if (not defined $actual_value) {
    0          
    0          
    0          
571 0 0       0 $is_default ? 1 : 0
572             } elsif (not ref $actual_value) {
573 0         0 $actual_value eq $formal_value
574             } elsif (ref $actual_value eq 'HASH') {
575 0         0 $actual_value->{$formal_value};
576             } elsif (ref $actual_value eq 'ARRAY') {
577 0     0   0 defined lsearch {$_ eq $formal_value} @$actual_value
  0         0  
578             } else {
579             undef
580 0         0 }
581             }
582             }
583              
584             # Verbatimly stolen from CGI::Simple
585             # XXX: not used?
586             sub url_decode {
587 0     0 0 0 my ( $self, $decode ) = @_;
588 0 0       0 return () unless defined $decode;
589 0         0 $decode =~ tr/+/ /;
590 0         0 $decode =~ s/%([a-fA-F0-9]{2})/ pack "C", hex $1 /eg;
  0         0  
591             # XXX: should set utf8 flag too?
592 0         0 return $decode;
593             }
594              
595             sub url_encode {
596 18     18 0 754 my ( $self, $encode ) = @_;
597 18 50       44 return () unless defined $encode;
598              
599 18 100       55 if (Encode::is_utf8($encode)) {
600 3         10 $encode = Encode::encode_utf8($encode);
601             }
602              
603             # XXX: Forward slash (and ':') is allowed, for cleaner url. This may break...
604             $encode
605 18         72 =~ s{([^A-Za-z0-9\-_.!~*'() /:])}{ uc sprintf "%%%02x",ord $1 }eg;
  47         140  
606 18         34 $encode =~ tr/ /+/;
607 18         62 return $encode;
608             }
609              
610             sub url_encode_kv {
611 0     0 0 0 my ($self, $k, $v) = @_;
612 0         0 url_encode($self, $k) . '=' . url_encode($self, $v);
613             }
614              
615             sub encode_query {
616 0     0 0 0 my ($self, $param, $sep) = @_;
617             # require URI;
618             # my $url = URI->new('http:');
619             # $url->query_form($item->{cf_PARAM});
620             # $url->query;
621 0 0       0 return $param unless ref $param;
622 0   0     0 join $sep // ';', do {
623 0 0       0 if (ref $param eq 'HASH') {
624             map {
625 0         0 url_encode_kv($self, $_, $param->{$_});
  0         0  
626             } keys %$param
627             } else {
628 0         0 my @param = @$param;
629 0         0 my @res;
630 0         0 while (my ($k, $v) = splice @param, 0, 2) {
631 0         0 my $ek = url_encode($self, $k);
632             push @res, $ek . '='. (url_encode($self, $_) // '')
633 0 0 0     0 for ref $v ? @$v : $v;
634             }
635 0         0 @res;
636             }
637             };
638             }
639              
640             sub callerinfo {
641 19   50 19 0 153 my ($pkg, $file, $line) = caller(shift // 1);
642 19         138 (file => $file, line => $line);
643             }
644              
645             sub ostream {
646 19 100 50 19 0 92 my $fn = ref $_[0] ? $_[0] : \ ($_[0] //= "");
647 19 50 50 3   372 open my $fh, '>' . ($_[1] // ''), $fn
  3         20  
  4         8  
  7         413  
648             or die "Can't create output memory stream: $!";
649 19         1590 $fh;
650             }
651              
652             sub read_file {
653 9     9 0 1271360 my ($fn, $layer) = @_;
654 9 50 50     627 open my $fh, '<' . ($layer // ''), $fn or die "Can't open '$fn': $!";
655 9         70 local $/;
656 9         222 scalar <$fh>;
657             }
658              
659             sub dispatch_all {
660 0     0 0 0 my ($this, $con, $prefix, $argSpec) = splice @_, 0, 4;
661 0 0       0 my ($nargs, @preargs) = ref $argSpec ? @$argSpec : $argSpec;
662 0         0 my @queue;
663 0         0 foreach my $item (@_) {
664 0 0       0 if (ref $item) {
665 0 0       0 print {$con} escape(splice @queue) if @queue;
  0         0  
666 0         0 my ($wname, @args) = @$item;
667 0 0       0 my $sub = $this->can('render_' . $prefix . $wname)
668             or croak "Can't find widget '$wname' in dispatch";
669 0   0     0 $sub->($this, $con, @preargs, splice(@args, 0, $nargs // 0), \@args);
670             } else {
671 0         0 push @queue, $item;
672             }
673             }
674 0 0       0 print {$con} escape(@queue) if @queue;
  0         0  
675             }
676              
677             sub dispatch_one {
678 0     0 0 0 my ($this, $con, $prefix, $nargs, $item) = @_;
679 0 0       0 if (ref $item) {
680 0         0 my ($wname, @args) = @$item;
681 0 0       0 my $sub = $this->can('render_' . $prefix . $wname)
682             or croak "Can't find widget '$wname' in dispatch";
683 0   0     0 $sub->($this, $con, splice(@args, 0, $nargs // 0), \@args);
684             } else {
685 0         0 print {$con} escape($item);
  0         0  
686             }
687             }
688              
689             sub con_error {
690 2     2 0 5 my ($con, $err, @args) = @_;
691 2 100 66     21 if ($con->can("raise") and my $sub = $con->can("error")) {
692 1         4 $sub->($con, $err, @args)
693             } else {
694 1         13 sprintf $err, @args;
695             }
696             }
697              
698             sub safe_render {
699 11     11 0 992 my ($this, $con, $wspec, @args) = @_;
700 11         34 my @nsegs = lexpand($wspec);
701 11 100       29 my $wname = join _ => map {defined $_ ? $_ : ''} @nsegs;
  15         58  
702 11 100       127 my $sub = $this->can("render_$wname")
703             or die con_error($con, "Can't find widget '%s'", $wname);
704 9         34 $sub->($this, $con, @args);
705             }
706              
707             sub mk_http_status {
708 29     29 0 72 my ($code) = @_;
709 29         178 require HTTP::Status;
710              
711 29         3544 my $message = HTTP::Status::status_message($code);
712 29         228 "Status: $code $message\015\012";
713             }
714              
715             sub list_isa {
716 1126     1126 0 4006 my ($pack, $all) = @_;
717 1126         2508 my $symtab = symtab($pack);
718 1126 100       4988 my $sym = $symtab->{ISA} or return;
719 990 50       1702 my $isa = *{$sym}{ARRAY} or return;
  990         3815  
720 990 100       6083 return @$isa unless $all;
721             map {
722 31         77 [$_, list_isa($_, $all)];
  54         115  
723             } @$isa;
724             }
725              
726             sub set_inc {
727 370     370 0 883 my ($pkg, $val) = @_;
728 370         1616 $pkg =~ s|::|/|g;
729 370   50     3684 $INC{$pkg.'.pm'} = $val || 1;
730             # $INC{$pkg.'.pmc'} = $val || 1;
731 370         871 $_[1];
732             }
733              
734             sub try_invoke {
735 725     725 0 1420 my $obj = shift;
736 725         1859 my ($method, @args) = lexpand(shift);
737 725         1380 my $default = shift;
738 725 100 66     5062 if (defined $obj
739             and my $sub = UNIVERSAL::can($obj, $method)) {
740 649         2753 $sub->($obj, @args);
741             } else {
742 76 100       688 wantarray ? () : $default;
743             }
744             }
745              
746             sub NIMPL {
747 0   0 0 0 0 my ($pack, $file, $line, $sub, $hasargs) = caller($_[0] // 1);
748 0         0 croak "Not implemented call of '$sub'";
749             }
750              
751             sub shallow_copy {
752 4 100   4 0 66 if (ref $_[0] eq 'HASH') {
    100          
    50          
    50          
753 2         3 +{%{$_[0]}};
  2         10  
754             } elsif (ref $_[0] eq 'ARRAY') {
755 1         2 +[@{$_[0]}];
  1         4  
756             } elsif (not ref $_[0]) {
757 0         0 my $copy = $_[0];
758             } elsif ($_[1]) {
759             # Pass thru unknown refs if 2nd arg is true.
760 1         7 $_[0];
761             } else {
762 0         0 croak "Unsupported data type for shallow_copy: " . ref $_[0];
763             }
764             }
765              
766             if (not is_debugging() or catch {require Sub::Name}) {
767 526     526   949 *subname = sub { my ($name, $sub) = @_; $sub }
  526         1083  
768             } else {
769             *subname = *Sub::Name::subname;
770             }
771              
772             sub incr_opt {
773 55     55 0 150 my ($key, $list) = @_;
774 55         107 my $hash = do {
775 55 100 66     450 if (@$list and defined $list->[0] and ref $list->[0] eq 'HASH') {
      100        
776 39         101 shift @$list;
777             } else {
778             +{}
779 16         44 }
780             };
781 55         136 $hash->{$key}++;
782 55         280 $hash;
783             }
784              
785             sub num_is_ge {
786 0 0 0 0 0 0 defined $_[0] and not ref $_[0] and $_[0] ne ''
      0        
      0        
787             and $_[0] =~ /^\d+$/ and $& >= $_[1];
788             }
789              
790             # Order preserving unique.
791             sub unique (@) {
792 4     4 0 29 my %dup;
793 4 100       8 map {$dup{$_}++ ? () : $_} @_;
  10         42  
794             }
795              
796             sub secure_text_plain {
797 4     4 0 5 shift;
798 4         18 ("Content-type" => "text/plain; charset=utf-8"
799             , "X-Content-Type-Options" => "nosniff" # To protect IE8~ from XSS.
800             );
801             }
802              
803             sub psgi_error {
804 4     4 0 11 my ($self, $status, $msg, @rest) = @_;
805 4         28 return [$status, [$self->secure_text_plain, @rest], [escape($msg)]];
806             }
807              
808             sub ixhash {
809 135     135 0 778 tie my %hash, 'Tie::IxHash', @_;
810 135         1952 \%hash;
811             }
812              
813             # Ported from: Rack::Utils.parse_nested_query
814             sub parse_nested_query {
815 246 100 100 246 0 8287 return {} unless defined $_[0] and $_[0] ne '';
816 78         157 my ($enc) = $_[1];
817 78   33     271 my $params = $_[2] // ixhash();
818 78 100       195 if (ref $_[0]) {
819 37 100       165 my @pairs = map {$enc ? map(Encode::decode($enc, $_), @$_) : @$_}
820 37 50       111 ref $_[0] eq 'ARRAY' ? $_[0] : [%{$_[0]}];
  0         0  
821 37         1062 while (my ($k, $v) = splice @pairs, 0, 2) {
822 61         133 normalize_params($params, $k, $v);
823             }
824             } else {
825 41         204 foreach my $p (split /[;&]/, $_[0]) {
826             my ($k, $v) = map {
827 68         176 s/\+/ /g;
  129         738  
828 129         312 my $raw = URI::Escape::uri_unescape($_);
829 129 100       1489 $enc ? Encode::decode($enc, $raw) : $raw;
830             } split /=/, $p, 2;
831 68 100       625 normalize_params($params, $k, $v) if defined $k;
832             }
833             }
834 75         304 $params;
835             }
836              
837             sub normalize_params {
838 214     214 0 419 my ($params, $name, $v) = @_;
839 214 100       921 my ($k) = $name =~ m(\A[\[\]]*([^\[\]]+)\]*)
840             or return;
841              
842 212         591 my $after = substr($name, length $&);
843              
844 212 100 100     731 if ($after eq '') {
    100          
    100          
845 90         384 $params->{$k} = $v;
846             } elsif ($after eq "[]") {
847 33   100     108 my $item = $params->{$k} //= [];
848 33 100 50     668 croak "expected ARRAY (got ".(ref $item || 'String').") for param `$k'"
849             unless ref $item eq 'ARRAY';
850 32         66 push @$item, $v;
851             } elsif ($after =~ m(^\[\]\[([^\[\]]+)\]$) or $after =~ m(^\[\](.+)$)) {
852 27         56 my $child_key = $1;
853 27   100     80 my $item = $params->{$k} //= [];
854 27 100 50     511 croak "expected ARRAY (got ".(ref $item || 'String').") for param `$k'"
855             unless ref $item eq 'ARRAY';
856 26 100 66     118 if (@$item and ref $item->[-1] eq 'HASH'
      100        
857             and not exists $item->[-1]->{$child_key}) {
858 8         51 normalize_params($item->[-1], $child_key, $v);
859             } else {
860 18         54 push @$item, normalize_params(ixhash(), $child_key, $v);
861             }
862             } else {
863 62   66     216 my $item = $params->{$k} //= ixhash();
864 62 100 50     1087 croak "expected HASH (got ".(ref $item || 'String').") for param `$k'"
865             unless ref $item eq 'HASH';
866 61         140 $params->{$k} = normalize_params($item, $after, $v);
867             }
868              
869 207         2251 $params;
870             }
871              
872             sub pkg2pm {
873 239     239 0 540 my ($pack) = @_;
874 239         1720 $pack =~ s{::|'}{/}g;
875 239         1107 "$pack.pm";
876             }
877              
878             #
879             # to put all functions into @EXPORT_OK.
880             #
881             {
882             our @EXPORT_OK = qw(define_const);
883             my $symtab = symtab(__PACKAGE__);
884             foreach my $name (grep {/^[a-z]/} keys %$symtab) {
885             my $glob = $symtab->{$name};
886             next unless *{$glob}{CODE};
887             push @EXPORT_OK, $name;
888             }
889             }
890              
891             1;