source: main/trunk/greenstone2/perllib/plugins/CommonUtil.pm@ 31492

Last change on this file since 31492 was 31492, checked in by kjdon, 7 years ago

renamed EncodingUtil to CommonUtil, BasePlugin to BaseImporter. The idea is that only top level plugins that you can specify in your collection get to have plugin in their name. Modified all other plugins to reflect these name changes

File size: 21.0 KB
Line 
1###########################################################################
2#
3# CommonUtil.pm -- base class for file and directory plugins - aims to
4# handle all encoding stuff, blocking stuff, to keep it in one place
5# A component of the Greenstone digital library software
6# from the New Zealand Digital Library Project at the
7# University of Waikato, New Zealand.
8#
9# Copyright (C) 2017 New Zealand Digital Library Project
10#
11# This program is free software; you can redistribute it and/or modify
12# it under the terms of the GNU General Public License as published by
13# the Free Software Foundation; either version 2 of the License, or
14# (at your option) any later version.
15#
16# This program is distributed in the hope that it will be useful,
17# but WITHOUT ANY WARRANTY; without even the implied warranty of
18# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19# GNU General Public License for more details.
20#
21# You should have received a copy of the GNU General Public License
22# along with this program; if not, write to the Free Software
23# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
24#
25###########################################################################
26
27package CommonUtil;
28
29use strict;
30no strict 'subs';
31no strict 'refs'; # allow filehandles to be variables and viceversa
32
33use encodings;
34use Unicode::Normalize 'normalize';
35
36use PrintInfo;
37use Encode;
38use Unicode::Normalize 'normalize';
39
40BEGIN {
41 @CommonUtil::ISA = ( 'PrintInfo' );
42}
43
44our $encoding_list =
45 [ { 'name' => "ascii",
46 'desc' => "{BaseImporter.encoding.ascii}" },
47 { 'name' => "utf8",
48 'desc' => "{BaseImporter.encoding.utf8}" },
49 { 'name' => "unicode",
50 'desc' => "{BaseImporter.encoding.unicode}" } ];
51
52
53my $e = $encodings::encodings;
54foreach my $enc (sort {$e->{$a}->{'name'} cmp $e->{$b}->{'name'}} keys (%$e))
55{
56 my $hashEncode =
57 {'name' => $enc,
58 'desc' => $e->{$enc}->{'name'}};
59
60 push(@{$encoding_list},$hashEncode);
61}
62
63our $encoding_plus_auto_list =
64 [ { 'name' => "auto",
65 'desc' => "{BaseImporter.filename_encoding.auto}" },
66 { 'name' => "auto-language-analysis",
67 'desc' => "{BaseImporter.filename_encoding.auto_language_analysis}" }, # textcat
68 { 'name' => "auto-filesystem-encoding",
69 'desc' => "{BaseImporter.filename_encoding.auto_filesystem_encoding}" }, # locale
70 { 'name' => "auto-fl",
71 'desc' => "{BaseImporter.filename_encoding.auto_fl}" }, # locale followed by textcat
72 { 'name' => "auto-lf",
73 'desc' => "{BaseImporter.filename_encoding.auto_lf}" } ]; # texcat followed by locale
74
75push(@{$encoding_plus_auto_list},@{$encoding_list});
76
77my $arguments =
78 [ { 'name' => "block_exp",
79 'desc' => "{BaseImporter.block_exp}",
80 'type' => "regexp",
81 'deft' => "",
82 'reqd' => "no" },
83 { 'name' => "no_blocking",
84 'desc' => "{BaseImporter.no_blocking}",
85 'type' => "flag",
86 'reqd' => "no"},
87 { 'name' => "filename_encoding",
88 'desc' => "{BaseImporter.filename_encoding}",
89 'type' => "enum",
90 'deft' => "auto",
91 'list' => $encoding_plus_auto_list,
92 'reqd' => "no" }
93 ];
94
95my $options = { 'name' => "CommonUtil",
96 'desc' => "{CommonUtil.desc}",
97 'abstract' => "yes",
98 'inherits' => "no",
99 'args' => $arguments };
100
101
102sub new {
103
104 my ($class) = shift (@_);
105 my ($pluginlist,$inputargs,$hashArgOptLists,$auxiliary) = @_;
106 push(@$pluginlist, $class);
107
108 push(@{$hashArgOptLists->{"ArgList"}},@{$arguments});
109 push(@{$hashArgOptLists->{"OptList"}},$options);
110
111 my $self = new PrintInfo($pluginlist, $inputargs, $hashArgOptLists,$auxiliary);
112
113 return bless $self, $class;
114
115}
116
117sub init {
118 my $self = shift (@_);
119 my ($verbosity, $outhandle, $failhandle) = @_;
120
121 print STDERR "guess encoding = ".$self->guess_filesystem_encoding()."\n";
122 print STDERR "get encoding = ".$self->get_filesystem_encoding()."\n";
123
124 # verbosity is passed through from the processor
125 $self->{'verbosity'} = $verbosity;
126
127 # as are the outhandle and failhandle
128 $self->{'outhandle'} = $outhandle if defined $outhandle;
129 $self->{'failhandle'} = $failhandle;
130
131}
132
133# converts raw filesystem filename to perl unicode format
134sub raw_filename_to_unicode {
135 my $self = shift (@_);
136 my ($file) = @_;
137
138 my $unicode_file = "";
139 ### need it in perl unicode, not raw filesystem
140 my $filename_encoding = $self->guess_filesystem_encoding();
141
142 # copied this from set_Source_metadata in BaseImporter
143 if ((defined $filename_encoding) && ($filename_encoding ne "ascii")) {
144 # Use filename_encoding to map raw filename to a Perl unicode-aware string
145 $unicode_file = decode($filename_encoding,$file);
146 }
147 else {
148 # otherwise generate %xx encoded version of filename for char > 127
149 $unicode_file = &unicode::raw_filename_to_url_encoded($file);
150 }
151 return $unicode_file;
152
153}
154# just converts path as is to utf8.
155sub filepath_to_utf8 {
156 my $self = shift (@_);
157 my ($file, $file_encoding) = @_;
158 my $filemeta = $file;
159
160 my $filename_encoding = $self->{'filename_encoding'}; # filename encoding setting
161
162 # Whenever filename-encoding is set to any of the auto settings, we
163 # check if the filename is already in UTF8. If it is, then we're done.
164 if($filename_encoding =~ m/auto/) {
165 if(&unicode::check_is_utf8($filemeta))
166 {
167 $filename_encoding = "utf8";
168 return $filemeta;
169 }
170 }
171
172 # Auto setting, but filename is not utf8
173 if ($filename_encoding eq "auto")
174 {
175 # try textcat
176 $filename_encoding = $self->textcat_encoding($filemeta);
177
178 # check the locale next
179 $filename_encoding = $self->locale_encoding() if $filename_encoding eq "undefined";
180
181
182 # now try the encoding of the document, if available
183 if ($filename_encoding eq "undefined" && defined $file_encoding) {
184 $filename_encoding = $file_encoding;
185 }
186
187 }
188
189 elsif ($filename_encoding eq "auto-language-analysis")
190 {
191 $filename_encoding = $self->textcat_encoding($filemeta);
192
193 # now try the encoding of the document, if available
194 if ($filename_encoding eq "undefined" && defined $file_encoding) {
195 $filename_encoding = $file_encoding;
196 }
197 }
198
199 elsif ($filename_encoding eq "auto-filesystem-encoding")
200 {
201 # try locale
202 $filename_encoding = $self->locale_encoding();
203 }
204
205 elsif ($filename_encoding eq "auto-fl")
206 {
207 # filesystem-encoding (locale) then language-analysis (textcat)
208 $filename_encoding = $self->locale_encoding();
209
210 # try textcat
211 $filename_encoding = $self->textcat_encoding($filemeta) if $filename_encoding eq "undefined";
212
213 # else assume filename encoding is encoding of file content, if that's available
214 if ($filename_encoding eq "undefined" && defined $file_encoding) {
215 $filename_encoding = $file_encoding;
216 }
217 }
218
219 elsif ($filename_encoding eq "auto-lf")
220 {
221 # language-analysis (textcat) then filesystem-encoding (locale)
222 $filename_encoding = $self->textcat_encoding($filemeta);
223
224 # guess filename encoding from encoding of file content, if available
225 if ($filename_encoding eq "undefined" && defined $file_encoding) {
226 $filename_encoding = $file_encoding;
227 }
228
229 # try locale
230 $filename_encoding = $self->locale_encoding() if $filename_encoding eq "undefined";
231 }
232
233 # if still undefined, use utf8 as fallback
234 if ($filename_encoding eq "undefined") {
235 $filename_encoding = "utf8";
236 }
237
238 #print STDERR "**** UTF8 encoding the filename $filemeta ";
239
240 # if the filename encoding is set to utf8 but it isn't utf8 already--such as when
241 # 1. the utf8 fallback is used, or 2. if the system locale is used and happens to
242 # be always utf8 (in which case the filename's encoding is also set as utf8 even
243 # though the filename need not be if it originates from another system)--in such
244 # cases attempt to make the filename utf8 to match.
245 if($filename_encoding eq "utf8" && !&unicode::check_is_utf8($filemeta)) {
246 &unicode::ensure_utf8(\$filemeta);
247 }
248
249 # convert non-unicode encodings to utf8
250 if ($filename_encoding !~ m/(?:ascii|utf8|unicode)/) {
251 $filemeta = &unicode::unicode2utf8(
252 &unicode::convert2unicode($filename_encoding, \$filemeta)
253 );
254 }
255
256 #print STDERR " from encoding $filename_encoding -> $filemeta\n";
257 return $filemeta;
258}
259
260# gets the filename with no path, converts to utf8, and then dm safes it.
261# filename_encoding set by user
262sub filename_to_utf8_metadata
263{
264 my $self = shift (@_);
265 my ($file, $file_encoding) = @_;
266
267 my $outhandle = $self->{'outhandle'};
268
269 print $outhandle "****!!!!**** BaseImporter::filename_to_utf8_metadata now deprecated\n";
270 my ($cpackage,$cfilename,$cline,$csubr,$chas_args,$cwantarray) = caller(0);
271 print $outhandle "Calling method: $cfilename:$cline $cpackage->$csubr\n";
272
273 my ($filemeta) = $file =~ /([^\\\/]+)$/; # getting the tail of the filepath (skips all string parts containing slashes upto the end)
274 $filemeta = $self->filepath_to_utf8($filemeta, $file_encoding);
275
276 return $filemeta;
277}
278
279sub locale_encoding {
280 my $self = shift(@_);
281
282 if (!defined $self->{'filesystem_encoding'}) {
283 $self->{'filesystem_encoding'} = $self->get_filesystem_encoding();
284 }
285
286 #print STDERR "*** filename encoding determined based on locale: " . $self->{'filesystem_encoding'} . "\n";
287 return $self->{'filesystem_encoding'}; # can be the string "undefined"
288}
289
290
291sub textcat_encoding {
292 my $self = shift(@_);
293 my ($filemeta) = @_;
294
295 # analyse filenames without extensions and digits (and trimmed of
296 # surrounding whitespace), so that irrelevant chars don't confuse
297 # textcat
298 my $strictfilemeta = $filemeta;
299 $strictfilemeta =~ s/\.[^\.]+$//g;
300 $strictfilemeta =~ s/\d//g;
301 $strictfilemeta =~ s/^\s*//g;
302 $strictfilemeta =~ s/\s*$//g;
303
304 my $filename_encoding = $self->encoding_from_language_analysis($strictfilemeta);
305 if(!defined $filename_encoding) {
306 $filename_encoding = "undefined";
307 }
308
309 return $filename_encoding; # can be the string "undefined"
310}
311
312# performs textcat
313sub encoding_from_language_analysis {
314 my $self = shift(@_);
315 my ($text) = @_;
316
317 my $outhandle = $self->{'outhandle'};
318 my $best_encoding = undef;
319
320 # get the language/encoding of the textstring using textcat
321 require textcat; # Only load the textcat module if it is required
322 $self->{'textcat'} = new textcat() unless defined($self->{'textcat'});
323 my $results = $self->{'textcat'}->classify_cached_filename(\$text);
324
325
326 if (scalar @$results < 0) {
327 return undef;
328 }
329
330 # We have some results, we choose the first
331 my ($language, $encoding) = $results->[0] =~ /^([^-]*)(?:-(.*))?$/;
332
333 $best_encoding = $encoding;
334 if (!defined $best_encoding) {
335 return undef;
336 }
337
338 if (defined $best_encoding && $best_encoding =~ m/^iso_8859/ && &unicode::check_is_utf8($text)) {
339 # the text is valid utf8, so assume that's the real encoding (since textcat is based on probabilities)
340 $best_encoding = 'utf8';
341 }
342
343
344 # check for equivalents where textcat doesn't have some encodings...
345 # eg MS versions of standard encodings
346 if (defined $best_encoding && $best_encoding =~ /^iso_8859_(\d+)/) {
347 my $iso = $1; # which variant of the iso standard?
348 # iso-8859 sets don't use chars 0x80-0x9f, windows codepages do
349 if ($text =~ /[\x80-\x9f]/) {
350 # Western Europe
351 if ($iso == 1 or $iso == 15) { $best_encoding = 'windows_1252' }
352 elsif ($iso == 2) {$best_encoding = 'windows_1250'} # Central Europe
353 elsif ($iso == 5) {$best_encoding = 'windows_1251'} # Cyrillic
354 elsif ($iso == 6) {$best_encoding = 'windows_1256'} # Arabic
355 elsif ($iso == 7) {$best_encoding = 'windows_1253'} # Greek
356 elsif ($iso == 8) {$best_encoding = 'windows_1255'} # Hebrew
357 elsif ($iso == 9) {$best_encoding = 'windows_1254'} # Turkish
358 }
359 }
360
361 if (defined $best_encoding && $best_encoding !~ /^(ascii|utf8|unicode)$/ &&
362 !defined $encodings::encodings->{$best_encoding})
363 {
364 if ($self->{'verbosity'}) {
365 gsprintf($outhandle, "BaseImporter: {ReadTextFile.unsupported_encoding}\n", $text, $best_encoding, "undef");
366 }
367 $best_encoding = undef;
368 }
369
370 return $best_encoding;
371}
372
373
374
375sub deduce_filename_encoding
376{
377 my $self = shift (@_);
378 my ($file,$metadata,$plugin_filename_encoding) = @_;
379
380 my $gs_filename_encoding = $metadata->{"gs.filenameEncoding"};
381 my $deduced_filename_encoding = undef;
382
383 # Start by looking for manually assigned metadata
384 if (defined $gs_filename_encoding) {
385 if (ref ($gs_filename_encoding) eq "ARRAY") {
386 my $outhandle = $self->{'outhandle'};
387
388 $deduced_filename_encoding = $gs_filename_encoding->[0];
389
390 my $num_vals = scalar(@$gs_filename_encoding);
391 if ($num_vals>1) {
392 print $outhandle "Warning: gs.filenameEncoding multiply defined for $file\n";
393 print $outhandle " Selecting first value: $deduced_filename_encoding\n";
394 }
395 }
396 else {
397 $deduced_filename_encoding = $gs_filename_encoding;
398 }
399 }
400
401 if (!defined $deduced_filename_encoding || ($deduced_filename_encoding =~ m/^\s*$/)) {
402 # Look to see if plugin specifies this value
403
404 if (defined $plugin_filename_encoding) {
405 # First look to see if we're using any of the "older" (i.e. deprecated auto-... plugin options)
406 if ($plugin_filename_encoding =~ m/^auto-.*$/) {
407 my $outhandle = $self->{'outhandle'};
408 print $outhandle "Warning: $plugin_filename_encoding is no longer supported\n";
409 print $outhandle " default to 'auto'\n";
410 $self->{'filename_encoding'} = $plugin_filename_encoding = "auto";
411 }
412
413 if ($plugin_filename_encoding ne "auto") {
414 # We've been given a specific filenamne encoding
415 # => so use it!
416 $deduced_filename_encoding = $plugin_filename_encoding;
417 }
418 }
419 }
420
421 if (!defined $deduced_filename_encoding || ($deduced_filename_encoding =~ m/^\s*$/)) {
422
423 # Look to file system to provide a character encoding
424
425 # If Windows NTFS, then -- assuming we work with long file names got through
426 # Win32::GetLongFilePath() -- then the underlying file system is UTF16
427
428 if (($ENV{'GSDLOS'} =~ m/^windows$/i) && ($^O ne "cygwin")) {
429 # Can do better than working with the DOS character encoding returned by locale
430 $deduced_filename_encoding = "unicode";
431 }
432 else {
433 # Unix of some form or other
434
435 # See if we can determine the file system encoding through locale
436 $deduced_filename_encoding = $self->locale_encoding();
437
438 # if locale shows us filesystem is utf8, check to see filename is consistent
439 # => if not, then we have an "alien" filename on our hands
440
441 if (defined $deduced_filename_encoding && $deduced_filename_encoding =~ m/^utf-?8$/i) {
442 if (!&unicode::check_is_utf8($file)) {
443 # "alien" filename, so revert
444 $deduced_filename_encoding = undef;
445 }
446 }
447 }
448 }
449
450# if (!defined $deduced_filename_encoding || ($deduced_filename_encoding =~ m/^\s*$/)) {
451# # Last chance, apply textcat to deduce filename encoding
452# $deduced_filename_encoding = $self->textcat_encoding($file);
453# }
454
455 if ($self->{'verbosity'}>3) {
456 my $outhandle = $self->{'outhandle'};
457
458 if (defined $deduced_filename_encoding) {
459 print $outhandle " Deduced filename encoding as: $deduced_filename_encoding\n";
460 }
461 else {
462 print $outhandle " No filename encoding deduced\n";
463 }
464 }
465
466 return $deduced_filename_encoding;
467}
468
469
470sub guess_filesystem_encoding
471{
472 my $self = shift (@_);
473 # Look to file system to provide a character encoding
474 my $deduced_filename_encoding = "";
475 # If Windows NTFS, then -- assuming we work with long file names got through
476 # Win32::GetLongFilePath() -- then the underlying file system is UTF16
477
478 if (($ENV{'GSDLOS'} =~ m/^windows$/i) && ($^O ne "cygwin")) {
479 # Can do better than working with the DOS character encoding returned by locale
480 $deduced_filename_encoding = "unicode";
481 }
482 else {
483 # Unix of some form or other
484
485 # See if we can determine the file system encoding through locale
486 $deduced_filename_encoding = $self->locale_encoding(); #utf8??
487
488 }
489 print STDERR "guessing filesystem encoding is $deduced_filename_encoding\n";
490 return $deduced_filename_encoding;
491}
492
493
494# uses locale
495sub get_filesystem_encoding
496{
497
498 my $self = shift(@_);
499
500 my $outhandle = $self->{'outhandle'};
501 my $filesystem_encoding = undef;
502
503 eval {
504 # Works for Windows as well, returning the DOS code page in use
505 use POSIX qw(locale_h);
506
507 # With only one parameter, setlocale retrieves the
508 # current value
509 my $current_locale = setlocale(LC_CTYPE);
510
511 my $char_encoding = undef;
512 if ($current_locale =~ m/\./) {
513 ($char_encoding) = ($current_locale =~ m/^.*\.(.*?)$/);
514 $char_encoding = lc($char_encoding);
515 }
516 else {
517 if ($current_locale =~ m/^(posix|c)$/i) {
518 $char_encoding = "ascii";
519 }
520 }
521
522 if (defined $char_encoding) {
523 if ($char_encoding =~ m/^(iso)(8859)-?(\d{1,2})$/) {
524 $char_encoding = "$1\_$2\_$3";
525 }
526
527 $char_encoding =~ s/-/_/g;
528 $char_encoding =~ s/^utf_8$/utf8/;
529
530 if ($char_encoding =~ m/^\d+$/) {
531 if (defined $encodings::encodings->{"windows_$char_encoding"}) {
532 $char_encoding = "windows_$char_encoding";
533 }
534 elsif (defined $encodings::encodings->{"dos_$char_encoding"}) {
535 $char_encoding = "dos_$char_encoding";
536 }
537 }
538
539 if (($char_encoding =~ m/(?:ascii|utf8|unicode)/)
540 || (defined $encodings::encodings->{$char_encoding})) {
541 $filesystem_encoding = $char_encoding;
542 }
543 else {
544 print $outhandle "Warning: Unsupported character encoding '$char_encoding' from locale '$current_locale'\n";
545 }
546 }
547
548
549 };
550 if ($@) {
551 print $outhandle "$@\n";
552 print $outhandle "Warning: Unable to establish locale. Will assume filesystem is UTF-8\n";
553
554 }
555
556 return $filesystem_encoding;
557}
558
559
560
561# write_file -- used by ConvertToPlug, for example in post processing
562#
563# where should this go, is here the best place??
564sub utf8_write_file {
565 my $self = shift (@_);
566 my ($textref, $filename) = @_;
567
568 if (!open (FILE, ">:utf8", $filename)) {
569 gsprintf(STDERR, "ConvertToPlug::write_file {ConvertToPlug.could_not_open_for_writing} ($!)\n", $filename);
570 die "\n";
571 }
572 print FILE $$textref;
573
574 close FILE;
575}
576
577sub block_raw_filename {
578
579 my $self = shift (@_);
580 my ($block_hash,$filename_full_path) = @_;
581
582 my $unicode_filename = $self->raw_filename_to_unicode($filename_full_path);
583 return $self->block_filename($block_hash, $unicode_filename);
584}
585
586# block unicode string filename
587sub block_filename
588{
589 my $self = shift (@_);
590 my ($block_hash,$filename_full_path) = @_;
591 print STDERR "in block filename $filename_full_path\n";
592 print STDERR &unicode::debug_unicode_string($filename_full_path)."\n";
593
594 if (($ENV{'GSDLOS'} =~ m/^windows$/) && ($^O ne "cygwin")) {
595 # block hash contains long names, lets make sure that we were passed a long name
596 $filename_full_path = &util::upgrade_if_dos_filename($filename_full_path);
597 # lower case the entire thing, eg for cover.jpg when its actually cover.JPG
598 my $lower_filename_full_path = lc($filename_full_path);
599 $block_hash->{'file_blocks'}->{$lower_filename_full_path} = 1;
600
601 }
602 elsif ($ENV{'GSDLOS'} =~ m/^darwin$/) {
603 # we need to normalize the filenames
604 my $composed_filename_full_path = normalize('C', $filename_full_path);
605 print STDERR "darwin, composed filename =". &unicode::debug_unicode_string($composed_filename_full_path)."\n";
606 $block_hash->{'file_blocks'}->{$composed_filename_full_path} = 1;
607 }
608
609 else {
610 $block_hash->{'file_blocks'}->{$filename_full_path} = 1;
611 }
612}
613
614
615# filename is raw filesystem name
616sub raw_file_is_blocked {
617 my $self = shift (@_);
618 my ($block_hash, $filename_full_path) = @_;
619
620 my $unicode_filename_full_path = $self->raw_filename_to_unicode($filename_full_path);
621 return $self->file_is_blocked($block_hash, $unicode_filename_full_path);
622}
623
624# filename must be perl unicode string
625sub file_is_blocked {
626 my $self = shift (@_);
627 my ($block_hash, $filename_full_path) = @_;
628
629 #
630 print STDERR "in file is blocked $filename_full_path\n";
631 print STDERR &unicode::debug_unicode_string($filename_full_path)."\n";
632 if (($ENV{'GSDLOS'} =~ m/^windows$/) && ($^O ne "cygwin")) {
633 # convert to long filenames if needed
634 $filename_full_path = &util::upgrade_if_dos_filename($filename_full_path);
635 # all block paths are lowercased.
636 my $lower_filename = lc ($filename_full_path);
637 if (defined $block_hash->{'file_blocks'}->{$lower_filename}) {
638 $self->{'num_blocked'} ++;
639 return 1;
640 }
641 }
642 elsif ($ENV{'GSDLOS'} =~ m/^darwin$/) {
643
644 # on mac, we want composed form in the block hash
645 my $composed_form = normalize('C', $filename_full_path);
646 print STDERR "gsdlos = darwin, composed = ". &unicode::debug_unicode_string($composed_form) ."\n";
647 if (defined $block_hash->{'file_blocks'}->{$composed_form}) {
648 $self->{'num_blocked'} ++;
649 print STDERR "BLOCKED 1\n";
650 return 1;
651 }
652 }
653
654 else {
655 if (defined $block_hash->{'file_blocks'}->{$filename_full_path}) {
656 $self->{'num_blocked'} ++;
657 print STDERR "BLOCKED\n";
658 return 1;
659 }
660 }
661 # check Directory plugin's own block_exp
662 if ($self->{'block_exp'} ne "" && $filename_full_path =~ /$self->{'block_exp'}/) {
663 $self->{'num_blocked'} ++;
664 return 1; # blocked
665 }
666 print STDERR "NOT BLOCKED\n";
667 return 0;
668}
669
670
6711;
672
Note: See TracBrowser for help on using the repository browser.