File Coverage

blib/lib/Sdict.pm
Criterion Covered Total %
statement 266 1635 16.2
branch 48 592 8.1
condition 2 110 1.8
subroutine 26 67 38.8
pod 0 50 0.0
total 342 2454 13.9


line stmt bran cond sub pod time code
1             # $RCSfile: Sdict.pm,v $
2             # $Author: swaj $
3             # $Revision: 1.36.2.2 $
4             #
5             # Copyright (c) Alexey Semenoff 2001-2007. All rights reserved.
6             # Distributed under GNU Public License.
7             #
8              
9              
10 2     2   13942 use 5.008;
  2         6  
  2         83  
11 2     2   11 use strict;
  2         4  
  2         135  
12 2     2   13 use warnings;
  2         9  
  2         98  
13              
14             package Sdict;
15              
16 2     2   1978 use Encode qw / encode decode from_to /;
  2         27392  
  2         215  
17 2     2   1783 use IO::File;
  2         23059  
  2         286  
18 2     2   2432 use Getopt::Long;
  2         29512  
  2         12  
19 2     2   2397 use Data::Dumper;
  2         12894  
  2         216  
20              
21             require Exporter;
22              
23 2         609 use vars qw(
24             @ISA
25             @EXPORT
26             @EXPORT_OK
27             %EXPORT_TAGS
28             $VERSION
29             $PACKAGE
30             $debug
31             $errstr
32             %COMPRESSION
33              
34             $W_LANG_POS
35             $A_LANG_POS
36             $WORDS_TOT_PTR_POS
37             $SINDEX_TOT_PTR_POS
38             $SINDEX_PTR_POS
39             $FINDEX_PTR_POS
40             $ARTICLES_PTR_POS
41             $COMPRESSOR_POS
42             $TITLE_PTR_POS
43             $COPYRIGHT_PTR_POS
44             $VERSION_PTR_POS
45             $sort_table
46             $sort_table_pl
47              
48             $HDR2_SIG_POS
49             $BIN1_PTR_POS
50 2     2   21 );
  2         4  
51              
52             $VERSION = '3.0';
53              
54             @ISA = qw(Exporter);
55              
56             @EXPORT = qw(
57             &prinfo
58             &prerror
59             );
60              
61             use constant {
62              
63 2         1053 COMPRESSOR_NONE => 'none' ,
64             COMPRESSOR_GZIP => 'gzip' ,
65             COMPRESSOR_BZIP2 => 'bzip2' ,
66             GZIP_COMPRESSION_LEVEL => 9 ,
67             BZIP2_COMPRESSION_LEVEL => 9 ,
68              
69             SDICT_SIG => 'sdct' ,
70             SDICT_HEADER_SIZE => 52 ,
71             SDICT_SOURCE_FILE_SEP => '___' ,
72             SDICT_SOURCE_FILE_SEP_O => '___' ,
73             SDICT_WORD_MAX_SIZE => 65535 - 8 ,
74             SDICT_ART_MAX_SIZE => 4294967295 - 4 ,
75              
76             SDICT_SHORT_NDX_LEN => 3 ,
77             SDICT_SHORT_NDX_LEN_MAX => 15 ,
78             SINDEX_ITEM_LEN => 3 * 4 + 4 , # SDICT_SHORT_NDX_LEN * 4 + 4,
79              
80             SDICT_FILE_EXT => '.dct' ,
81             SDICT_SEARCH_FORWARD => 15000 ,
82             SDICT_SINDEX_WARN => 1940000 ,
83             SDICT_HDR2_SIG => 4061299974 , # 0xf2128506
84              
85             SDICT_IMG_PNG => 1 ,
86             SDICT_IMG_GIF => 2 ,
87             SDICT_IMG_JPEG => 3 ,
88             SDICT_IMG_JB2 => 4 ,
89             SDICT_IMG_IW44 => 5 ,
90             SDICT_IMG_DJVU => 1001 ,
91             SDICT_SND_MP3 => 32 , # 0x20
92             SDICT_SND_WAV => 33 , # 0x21
93 2     2   13 };
  2         4  
94              
95             sub prerror (@);
96             sub prinfo (@);
97             sub help ($);
98             sub help_and_quit ($);
99             sub prline (@);
100             sub init ($%);
101             sub parse_args($);
102             sub convert($);
103             sub print_dct_info ($);
104              
105              
106             BEGIN {
107 2     2   20 $_=$0;
108 2         11 s|^(.+)/.*|$1|;
109 2         11 push @INC, (
110             $_,
111             "$_/lib",
112             "$_/../lib",
113             "$_/.."
114             ) ;
115              
116 2         17 %COMPRESSION = qw / none 0 gzip 1 bzip2 2 /;
117              
118 2         3 $W_LANG_POS = 4;
119 2         4 $A_LANG_POS = 7;
120 2         2 $COMPRESSOR_POS = hex ( "0xa" );
121 2         5 $WORDS_TOT_PTR_POS = hex ( "0xb" );
122 2         3 $SINDEX_TOT_PTR_POS = hex ( "0xf" );
123 2         3 $TITLE_PTR_POS = hex ( "0x13" );
124 2         3 $COPYRIGHT_PTR_POS = hex ( "0x17" );
125 2         2 $VERSION_PTR_POS = hex ( "0x1b" );
126 2         18 $SINDEX_PTR_POS = hex ( "0x1f" );
127 2         3 $FINDEX_PTR_POS = hex ( "0x23" );
128 2         4 $ARTICLES_PTR_POS = hex ( "0x27" );
129              
130 2         3 $HDR2_SIG_POS = hex ( "0x2b" );
131 2         7 $BIN1_PTR_POS = hex ( "0x30" );
132              
133 2         3 $debug = 0;
134 2         133 $PACKAGE = __PACKAGE__;
135             };
136              
137              
138             sub new () {
139 1     1 0 313 my $class = shift;
140 1         2 my $self = {};
141 1         4 $self->{ init } = 0;
142              
143 1         4 my $cpu = q{};
144 1         3 $self->{ big_endian } = 0;
145              
146 2     2   12 eval { use Config; $cpu = $Config{byteorder}; };
  2         2  
  2         39661  
  1         1  
  1         1030  
147              
148 1 50 33     3316 if ($@ || !$cpu) {
149 0         0 warn "unable to get CPU type";
150             }
151              
152 1 50 33     12 $self->{ big_endian } = 1 if ($cpu eq '4321' || $cpu eq '87654321');
153              
154             # TODO: add support for big-endian
155 1 50       6 die "\nERROR: Big-endian systems are not yet supported!\n" if ($self->{ big_endian });
156              
157 1         6 return bless $self, $class;
158             }
159              
160              
161             sub help ($) {
162              
163 0     0 0 0 print <
164             ------------------------------------------------------------------------------
165             Usage: $0
166             --compile | The main action which
167             --decompile | should be one of these
168             --analyze[=max] | commands
169             --printinfo
170              
171             --input-file=filename Input filename
172             [ --output-file=filename ] Output filename
173              
174             [ --sindex-levels=3-15 ] Number of short index levels, default is 3
175              
176             Sort words before packing:
177             [ --sort=sort_table[.pl] ] - table sorting
178             [ --sort=Unicode::Collate ] - use Unicode::Collate for sorting
179             [ --sort=numeric ] - numeric sorting
180              
181             [ --compression=none|gzip ] Use compression; default is none,
182             gzip is better choice
183             [ --lowercase-alias ] Duplicate word list with lowercase
184             aliases (useful for PDA)
185             [ --force-to-lowercase ] Force all words to lowercase first
186             [ --disable-duplicates ] Stop with an error if duplicate words found
187              
188              
189             [ --parse-embedded ] Handle embedded images
190             [ --images-dir=path ] Path to embedded images, default is './images'
191             [ --sounds-dir=path ] Path to embedded sounds, default is './sounds'
192             [ --try-djvu-first ] Use DJVU file if exists
193              
194             [ --fool-terminal ] Force to use non-Unicode terminal output
195             ------------------------------------------------------------------------------
196             EOS
197             }
198              
199              
200             sub help_and_quit ($) {
201 0     0 0 0 help ( shift );
202 0         0 exit 1;
203             }
204              
205              
206             sub prerror (@) {
207 0     0 0 0 print STDERR "\nERROR ($PACKAGE)! @_\n\n";
208             }
209              
210              
211             sub prinfo (@) {
212 20 50   20 0 2188 $debug && print "INFO ($PACKAGE): @_\n";
213             }
214              
215              
216             sub prline (@) {
217 0     0 0 0 print ">>> @_\n";
218             }
219              
220              
221             sub debug_on {
222 0     0 0 0 $debug = 1;
223             }
224              
225              
226             sub debug_off {
227 0     0 0 0 $debug = 0;
228             }
229              
230              
231             sub parse_args ($) {
232 0     0 0 0 my $class = shift;
233              
234             my (
235 0         0 $compile,
236             $decompile,
237             $infile,
238             $outfile,
239             $compressor,
240             $sort,
241             $slevels,
242             $analyze,
243             $lowercasealias,
244             $forcetolowercase,
245             $disableduplicates,
246             $printinfo,
247             $convertcharset,
248             $images_dir,
249             $sounds_dir,
250             $parse_embedded,
251             $try_djvu_first,
252             );
253              
254 0         0 GetOptions(
255              
256             "compile" => \$compile,
257             "decompile" => \$decompile,
258             "analyze=s" => \$analyze,
259             "input-file=s" => \$infile,
260             "output-file=s" => \$outfile,
261             "sort=s" => \$sort,
262             "compression=s" => \$compressor,
263             "sindex-levels=s" => \$slevels,
264             "lowercase-alias" => \$lowercasealias,
265             "force-to-lowercase" => \$forcetolowercase,
266             "disable-duplicates" => \$disableduplicates,
267             "printinfo" => \$printinfo,
268             "fool-terminal" => \$convertcharset,
269             "images-dir=s" => \$images_dir,
270             "sounds-dir=s" => \$sounds_dir,
271             "parse-embedded" => \$parse_embedded,
272             "try-djvu-first" => \$try_djvu_first,
273             );
274              
275 0         0 prinfo "Started, module version $VERSION";
276              
277 0 0 0     0 $class->help_and_quit if ( $compile && $decompile );
278 0 0 0     0 $class->help_and_quit unless ( $compile || $decompile || $analyze || $printinfo );
      0        
      0        
279 0 0       0 $class->help_and_quit if ( $infile eq q{} );
280              
281 0 0       0 $outfile = q{} unless defined ($outfile);
282              
283 0 0       0 if ( $outfile eq q{} ) {
284 0 0 0     0 $class->help_and_quit if ( !defined ($analyze) && !defined ($printinfo) );
285             }
286              
287 0         0 $class->{ infile } = $infile;
288 0         0 $class->{ outfile } = $outfile;
289              
290 0 0       0 $class->{ action } = 'compile' if ( $compile );
291 0 0       0 $class->{ action } = 'decompile' if ( $decompile );
292 0 0       0 $class->{ action } = 'analyze' if ( $analyze );
293 0 0       0 $class->{ action } = 'printinfo' if ( $printinfo );
294 0         0 $class->{ analyze_max } = $analyze;
295              
296 0 0       0 $class->help_and_quit unless ( $class->{ action } );
297              
298 0   0     0 $class->{ sort } = $sort || 0;
299 0   0     0 $class->{ convertcharset } = $convertcharset || 0;
300 0   0     0 $class->{ parse_embedded } = $parse_embedded || 0;
301 0   0     0 $class->{ try_djvu_first } = $try_djvu_first || 0;
302              
303              
304 0 0       0 unless ($compressor) {
    0          
    0          
    0          
305 0         0 $class->{ compressor } = COMPRESSOR_NONE;
306             }
307             elsif ( $compressor eq COMPRESSOR_NONE ) {
308 0         0 $class->{ compressor } = COMPRESSOR_NONE;
309             }
310             elsif ( $compressor eq COMPRESSOR_GZIP ) {
311 0         0 $class->{ compressor } = COMPRESSOR_GZIP;
312              
313 0         0 eval 'use Compress::Zlib';
314 0 0       0 if ( $@ ) {
315 0         0 prerror "Unable to load compression module 'Compress::Zlib' $@";
316 0         0 exit 1;
317             }
318              
319             }
320             elsif ( $compressor eq COMPRESSOR_BZIP2 ) {
321 0         0 $class->{ compressor } = COMPRESSOR_BZIP2;
322 0         0 eval 'use Compress::Bzip2';
323 0 0       0 if ( $@ ) {
324 0         0 prerror "Unable to load compression module 'Compress::Bzip2' $@";
325 0         0 exit 1;
326             }
327 0         0 prerror 'This compression method is not tested!';
328 0         0 exit 1;
329              
330             }
331             else {
332 0         0 prerror 'Wrong compression or short index levels value';
333 0         0 $class->help_and_quit;
334             }
335              
336              
337 0 0       0 unless ( $slevels ) {
338 0         0 $class->{ slevels } = SDICT_SHORT_NDX_LEN;
339             }
340             else {
341 0         0 $class->{ slevels } = $slevels;
342             }
343              
344 0 0 0     0 if ( ( $class->{ slevels } < SDICT_SHORT_NDX_LEN ) ||
345             ( $class->{ slevels } > SDICT_SHORT_NDX_LEN_MAX ) ) {
346 0         0 prerror "Invalid 'sindex-levels' value, must be between 3 and 15";
347 0         0 $class->help_and_quit;
348             }
349              
350 0 0 0     0 if ( $forcetolowercase && $lowercasealias ) {
351 0         0 prerror "Both '--force-to-lowercase' and '--lowercasealias' can't be specified in the same time";
352 0         0 $class->help_and_quit;
353             }
354              
355 0 0       0 unless ( $lowercasealias ) {
356 0         0 $class->{ lowercasealias } = 0;
357             }
358             else {
359 0         0 $class->{ lowercasealias } = $lowercasealias;
360             }
361              
362 0 0       0 unless ( $forcetolowercase ) {
363 0         0 $class->{ forcetolowercase } = 0;
364             }
365             else {
366 0         0 $class->{ forcetolowercase } = $forcetolowercase;
367             }
368              
369 0 0       0 unless ( $disableduplicates ) {
370 0         0 $class->{ disableduplicates } = 0;
371             }
372             else {
373 0         0 $class->{ disableduplicates } = $disableduplicates;
374             }
375              
376 0 0       0 unless ( $images_dir ) {
377 0         0 $class->{ images_dir } = 'images/';
378             }
379             else {
380 0         0 $class->{ images_dir } = $images_dir;
381             }
382              
383 0 0       0 unless ( $sounds_dir ) {
384 0         0 $class->{ sounds_dir } = 'sounds/';
385             }
386             else {
387 0         0 $class->{ sounds_dir } = $sounds_dir;
388             }
389              
390 0         0 $class->{ embedded_cur_num } = 0;
391 0         0 $class->{ embedded_cur_offset } = 0;
392 0         0 $class->{ embedded_total } = 0;
393 0         0 $class->{ embedded_offsets } = [];
394              
395 0         0 $class->{ init } = 1;
396 0         0 prinfo 'Initialization OK!';
397 0         0 return 1;
398             }
399              
400              
401             sub convert ($) {
402 0     0 0 0 my $class = shift;
403              
404 0 0       0 if ( $class->{ action } eq 'compile' ) {
    0          
    0          
405 0         0 return $class->compile;
406             }
407             elsif ( $class->{ action } eq 'decompile' ) {
408 0         0 return $class->decompile;
409             }
410             elsif ( $class->{ action } eq 'analyze' ) {
411 0         0 return $class->analyze;
412             }
413             }
414              
415              
416             sub init ($%) {
417 1     1 0 457 my ( $class, $params ) = @_[ 0, 1 ];
418 1         4 $class->{ infile } = $params->{ file };
419 1         4 $class->{ init } = 1;
420 1         3 return 1;
421             }
422              
423              
424             sub convert_charset_ai {
425 0     0 0 0 my ($class, $string) = @_;
426              
427 0 0       0 return unless ( $class->{ convertcharset } );
428              
429 0 0       0 my $charset_to = ( $class->{ header }->{ w_lang } eq 'ru' ) ? 'koi8-r' : 'iso-8859-1' ;
430 0         0 from_to ( $class->{ header }->{ title }, "utf8", $charset_to );
431 0         0 from_to ( $class->{ header }->{ copyright }, "utf8", $charset_to );
432             }
433              
434              
435             sub print_dct_info ($) {
436 0     0 0 0 my $class = $_[0];
437              
438 0 0       0 die "Unable load dictionary, file '$class->{ infile }'\n" unless $class->load_dictionary_fast;
439              
440             # print Dumper $class;
441              
442 0         0 $class->convert_charset_ai;
443              
444 0         0 my $size = (stat ($class->{ infile }))[7];
445              
446 0         0 print <
447             +------------------------------------------------------------------------------
448             | Dictionary information ($class->{ infile }, $size bytes):
449             |
450             | Title $class->{ header }->{ title }
451             | Copyright $class->{ header }->{ copyright }
452             | Languages $class->{ header }->{ w_lang }/$class->{ header }->{ a_lang }
453             | Version $class->{ header }->{ version }
454             | Word(s) $class->{ header }->{ words_total }
455             | Indices $class->{ slevels }+1
456             | Compression $class->{ compressor }
457             +------------------------------------------------------------------------------
458             EOS
459              
460              
461 0         0 $class->unload_dictionary;
462 0         0 return 1;
463             }
464              
465              
466             sub search_word ($$) {
467 2     2 0 76 my ( $class, $word ) = @_;
468              
469 2 50       7 if ( $word eq q{} ) {
470 0         0 prerror 'Wrong arguments';
471 0         0 return q{};
472             }
473              
474 2 50       8 unless ( defined ( $class->{ header } ) ) {
475 0         0 prerror 'Class is not initialized';
476 0         0 return q{};
477             }
478              
479 2         10 prinfo "Searching for '$word'";
480              
481 2         11 my $word_u = decode ( "utf8", $word );
482 2         76 my $ref;
483 2         6 my $search_pos = -1;
484              
485 2         5 my $len = length ( $word_u );
486 2         6 my $subw = substr ( $word_u, 0, 3 );
487              
488 2 50       6 return q{} unless $len;
489            
490 2         8 for ( my $i=1; $i<4; $i++ ) {
491              
492 6 100       19 if ( $i == 1 ) {
    100          
493 2         5 $ref = $class->{ sindex_1 };
494             }
495             elsif ( $i == 2 ) {
496 2         5 $ref = $class->{ sindex_2 };
497             }
498             else {
499 2         5 $ref = $class->{ sindex_3 };
500             }
501            
502 6         23 for my $j ( @$ref ) {
503 6         13 my ( $wo, $ndx ) = @$j;
504 6 100       27 if ( substr( $wo, 0, $i ) eq substr( $subw, 0, $i ) ) {
505             # prinfo "Found in '$i', wo: '$wo', ndx: '$ndx'";
506 1         2 $search_pos = $ndx;
507 1         6 next;
508             }
509             }
510             }
511              
512 2 100       6 if ( $search_pos < 0 ) {
513 1         13 prinfo 'Not found';
514 1         5 return q{};
515             }
516              
517             # prinfo "Scanning from pos '$search_pos'";
518              
519 1         3 my $findes_saved = $class->{ f_index_pos_cur };
520              
521 1         4 $class->{ f_index_pos_cur } = $search_pos + $class->{ f_index_pos };
522              
523 1         5 for ( my $ii=0; $ii < SDICT_SEARCH_FORWARD; $ii++ ) {
524 1         4 my $prev_pos = $class->{f_index_pos_cur};
525 1         3 my $nw = $class->get_next_word;
526              
527 1 50       5 if ( $nw eq q{} ) {
528 0         0 $class->{ f_index_pos_cur } = $findes_saved;
529 0         0 prinfo 'Not found';
530 0         0 return q{};
531             }
532              
533 1         4 $nw = decode ( "utf8", $nw );
534              
535 1 50       30 if ( substr ( $word_u, 0, 3 ) ne substr( $nw, 0, 3 ) ) {
536 0         0 prinfo 'Not found';
537 0         0 return q{};
538             }
539              
540 1 50       6 if ( $word_u eq $nw ) {
541              
542             my $art = $class->read_unit (
543             $class->{ cur_word_pos } +
544             $class->{ articles_pos }
545 1         7 );
546              
547 1 50       5 return q{} if ( $art eq q{} );
548 1         5 return $art;
549             }
550             }
551 0         0 prinfo 'Not found';
552 0         0 return q{};
553             }
554              
555              
556             sub load_dictionary ($) {
557 0     0 0 0 my $class = shift;
558            
559 0         0 prinfo 'Reading header';
560 0 0       0 return 0 unless $class->read_header;
561              
562 0         0 prinfo 'Reading full index';
563 0 0       0 return 0 unless $class->read_full_index;
564              
565 0         0 prinfo 'Reading short index';
566 0 0       0 return 0 unless $class->read_short_index;
567              
568 0         0 return 1;
569             }
570              
571              
572             sub load_dictionary_fast ($) {
573 1     1 0 8 my $class = shift;
574            
575 1         4 prinfo 'Reading header';
576 1 50       8 return 0 unless $class->read_header;
577              
578             # print Dumper $class; die;
579              
580 1         5 prinfo 'Reading short index fast';
581 1 50       7 return 0 unless $class->read_short_index_fast;
582              
583 1         4 $class->{ f_index_pos_cur } = $class->{ f_index_pos };
584            
585 1         8 return 1;
586             }
587              
588              
589             sub get_next_word ($) {
590 3     3 0 42 my $class = shift;
591 3         13 my $file = $class->{ infile_handler };
592 3         7 my $fpos = $class->{ f_index_pos_cur };
593 3         7 my $hdr = q{};
594              
595             my (
596 3         5 $next,
597             $aptr,
598             $wlen,
599             $word
600             );
601              
602 3 50       24 unless ( sysseek ( $file, $fpos, 0 ) ) {
603 0         0 prerror "Seek error: $!";
604 0         0 exit 1;
605             }
606              
607 3 50       34 unless (sysread ($file, $hdr, 8, 0)) {
608 0         0 prerror "Sysread error: $!";
609 0         0 exit 1;
610             }
611              
612 3         13 $next = unpack ( "S", substr ( $hdr, 0, 2 ) );
613              
614 3 50       7 unless ( $next ) {
615 0         0 prinfo 'Last word reached';
616 0         0 return q{};
617             }
618              
619 3         8 $aptr = unpack ( "L", substr ( $hdr, 4, 4 ) );
620              
621 3         6 $wlen = $next - 4 - 2 - 2;
622              
623 3 50       9 if ( $wlen < 0 ) {
624 0         0 prerror 'File format error';
625 0         0 exit 1;
626             }
627              
628 3 50       22 unless ( sysread ( $file, $word, $wlen, 0 ) ) {
629 0         0 prerror "Sysread error: $!";
630 0         0 exit 1;
631             }
632              
633 3         8 $class->{ cur_word } = $word;
634 3         6 $class->{ cur_word_pos } = $aptr;
635 3         7 $class->{ f_index_pos_cur } += $wlen + 8;
636              
637 3         11 return $word;
638             }
639              
640              
641             sub get_prev_word ($) {
642 0     0 0 0 my $class = shift;
643 0         0 my $file = $class->{ infile_handler };
644 0         0 my $fpos = $class->{ f_index_pos_cur };
645 0         0 my $hdr = q{};
646 0         0 my ( $next, $prev, $aptr, $wlen, $word );
647              
648              
649 0 0       0 unless ( sysseek( $file, $fpos, 0 ) ) {
650 0         0 prerror "Seek error: $!";
651 0         0 exit 1;
652             }
653              
654 0 0       0 unless ( sysread ( $file, $hdr, 8, 0 ) ) {
655 0         0 prerror "Sysread error: $!";
656 0         0 exit 1;
657             }
658              
659 0         0 $prev = unpack ( "S", substr ( $hdr, 2, 2 ) );
660              
661 0 0       0 unless ( $prev ) {
662 0         0 prinfo 'First word reached';
663 0         0 return q{};
664             }
665              
666 0 0       0 unless ( sysseek ( $file, $fpos - $prev, 0 ) ) {
667 0         0 prerror "Seek error: $!";
668 0         0 exit 1;
669             }
670              
671 0 0       0 unless ( sysread ( $file, $hdr, 8, 0 ) ) {
672 0         0 prerror "Sysread error: $!";
673 0         0 exit 1;
674             }
675              
676 0         0 $next = unpack ( "S", substr ( $hdr, 0, 2 ) );
677            
678 0         0 $aptr = unpack ( "L", substr ( $hdr, 4, 4 ) );
679              
680 0         0 $wlen = $next - 4 - 2 - 2;
681              
682 0 0       0 if ( $wlen < 0 ) {
683 0         0 prerror 'File format error';
684 0         0 exit 1;
685             }
686              
687 0 0       0 unless ( sysread ( $file, $word, $wlen, 0 ) ) {
688 0         0 prerror "Sysread error: $!";
689 0         0 exit 1;
690             }
691              
692 0         0 $class->{ cur_word } = $word;
693 0         0 $class->{ cur_word_pos } = $aptr;
694 0         0 $class->{ f_index_pos_cur } = $fpos - $prev;
695              
696 0         0 return $word;
697             }
698              
699              
700             sub read_short_index_fast ($) {
701 1     1 0 1 my $class = shift;
702 1         4 my $file = $class->{ infile_handler };
703              
704             # my $sindex_len = $class->{ header }->{ sindex_total } * SINDEX_ITEM_LEN;
705             # SINDEX_ITEM_LEN => 3 * 4 + 4 , # SDICT_SHORT_NDX_LEN * 4 + 4,
706              
707             my $sindex_len = $class->{ header }->{ sindex_total } *
708 1         4 ( $class->{ slevels } * 4 + 4 );
709              
710              
711 1         1 my $sindex = q{};
712 1         2 my $sindex_d = q{};
713             my (
714 1         2 $sword_u,
715             $word_ptr,
716             $fiunit,
717             $word,
718             $i
719             );
720              
721              
722 1         2 my %sindex_words = ();
723 1         2 my %temp_index = ();
724              
725 1 50       12 unless ( sysseek ( $file, $class->{ header }->{ sindex_ptr }, 0 ) ) {
726 0         0 prerror "Seek error: $!";
727 0         0 exit 1;
728             }
729              
730 1 50       16 unless ( sysread ( $file, $sindex, $sindex_len, 0 ) ) {
731 0         0 prerror "Sysread error: $!";
732 0         0 return q{};
733             }
734              
735             # my $co = unpack ( "C", $class->{ compressor } );
736             # warn ">$co< \n";
737             # exit ;
738              
739 1 50       7 if ( $class->{ compressor } eq COMPRESSOR_NONE ) {
    50          
    0          
740 0         0 prinfo 'No decompression needed';
741 0         0 $sindex_d = $sindex;
742              
743             }
744             elsif ($class->{ compressor } eq COMPRESSOR_GZIP ) {
745 1         2 prinfo 'Decompressing short index using gzip';
746 1         6 $sindex_d = uncompress ( $sindex, GZIP_COMPRESSION_LEVEL );
747              
748 1 50       81 unless ( $sindex_d ) {
749 0         0 prerror ("Decompression failed");
750 0         0 exit 1;
751             }
752              
753             }
754             elsif ( $class->{ compressor } eq COMPRESSOR_BZIP2 ) {
755 0         0 prinfo 'Decompressing short index using bgzip2';
756 0         0 $sindex_d = Compress::Bzip2::uncompress ( $sindex, GZIP_COMPRESSION_LEVEL );
757              
758 0 0       0 unless ( $sindex_d ) {
759 0         0 prerror ("Decompression failed");
760 0         0 exit 1;
761             }
762             }
763             else {
764 0         0 prerror 'Wrong compression';
765 0         0 exit 1;
766             }
767              
768 1         38 $i = 0;
769              
770 1         2 my @sindex_1 = ();
771 1         2 my @sindex_2 = ();
772 1         2 my @sindex_3 = ();
773              
774 1         2 my $sindex_skipped = 0;
775              
776 1         5 for ( $i=0; $i < $class->{ header }->{ sindex_total }; $i++ ) {
777             my $sword = substr (
778             $sindex_d,
779             # $i * SINDEX_ITEM_LEN,
780             $i * ( $class->{ slevels } * 4 + 4 ),
781             # SDICT_SHORT_NDX_LEN * 4
782 3         11 ( $class->{ slevels } * 4 )
783             );
784              
785 3         11 from_to ( $sword, "UTF-32LE", "utf8" );
786 3         184548 $sword_u = $sword;
787 3         11 $sword_u =~ s|\x0||g;
788 3         10 $sword_u = decode ( "utf8", $sword_u );
789              
790             $word_ptr = unpack (
791             "L",
792             substr (
793             $sindex_d,
794             # $i * SINDEX_ITEM_LEN + SDICT_SHORT_NDX_LEN * 4,
795 3         95 $i * ( $class->{ slevels } * 4 + 4 ) + ( $class->{ slevels } * 4 ),
796             4
797             )
798             );
799              
800 3 50       11 if ( length ( $sword_u ) == 1 ) {
    0          
    0          
801 3         19 push @sindex_1, [ $sword_u, $word_ptr ];
802             }
803             elsif ( length ( $sword_u ) == 2 ) {
804 0         0 push @sindex_2, [ $sword_u, $word_ptr ];
805             }
806             elsif ( length ( $sword_u ) == 3 ) {
807 0         0 push @sindex_3, [ $sword_u, $word_ptr ];
808             }
809             else {
810 0 0       0 if ( $class->{ slevels } > 3 ) {
811 0         0 $sindex_skipped++;
812             # ok!
813             }
814             else {
815 0         0 die "Sindex too big for '$sword_u'";
816             }
817             }
818             }
819              
820 1         5 $class->{ header }->{ sindex_total } -= $sindex_skipped ;
821              
822 1         4 $class->{ sindex_1 } = \@sindex_1;
823 1         3 $class->{ sindex_2 } = \@sindex_2;
824 1         5 $class->{ sindex_3 } = \@sindex_3;
825              
826             # print Dumper $class;
827 1         10 return 1;
828             }
829              
830              
831             sub unload_dictionary ($) {
832 1     1 0 43 my $class = shift;
833              
834 1         4 prinfo 'Unloading dictionary';
835 1         6 $class->{ words_list } = undef;
836 1         3 $class->{ words_hash } = undef;
837 1         4 $class->{ sindex_hash } = undef;
838 1         2 $class->{ header } = undef;
839              
840 1         8 $class->{ sindex_1 } = undef;
841 1         6 $class->{ sindex_2 } = undef;
842 1         2 $class->{ sindex_3 } = undef;
843              
844 1         4 $class->{ infile } = q{};
845 1         3 $class->{ init } = 0;
846              
847 1         133 return 1;
848             }
849              
850              
851             sub decompile ($) {
852 0     0 0 0 my $class = shift;
853              
854             my (
855 0         0 $w_lang,
856             $a_lang,
857             $title,
858             $copyright,
859             $version
860             );
861              
862 0         0 my $infile = $class->{ infile };
863 0         0 my $outfile = $class->{ outfile };
864              
865 0 0       0 unless ( open ( OF, "> $outfile" ) ) {
866 0         0 prerror "Unable create file '$outfile': $!";
867 0         0 exit 1;
868             }
869              
870 0         0 print OF "#\n# Converted from $infile by $0\n#\n";
871              
872 0         0 $class->{ outfile_handler } = *OF;
873              
874 0         0 prinfo 'Reading header';
875 0         0 $class->read_header;
876              
877 0         0 $title = $class->{ header }->{ title };
878 0         0 $copyright = $class->{ header }->{ copyright };
879 0         0 $version = $class->{ header }->{ version };
880 0         0 $w_lang = $class->{ header }->{ w_lang };
881 0         0 $a_lang = $class->{ header }->{ a_lang };
882              
883 0         0 print OF <
884            
885             title = $title
886             copyright = $copyright
887             version = $version
888             w_lang = $w_lang
889             a_lang = $a_lang
890            
891             #
892             # Begin of articles
893             #
894             EOS
895              
896 0         0 prinfo 'Reading full index';
897 0         0 $class->read_full_index;
898              
899 0         0 prinfo 'Dumping words';
900 0         0 $class->dump_all_words;
901              
902 0         0 close ( IF );
903 0         0 close ( OF );
904              
905 0         0 prinfo 'Done';
906 0         0 return 1;
907             }
908              
909              
910             sub read_header ($) {
911 1     1 0 3 my $class = shift;
912 1         3 my $hdr;
913              
914             my (
915 1         1 $w_lang,
916             $a_lang,
917             $compr,
918             $compr_method,
919             $tot_words,
920             $title_ptr,
921             $copyr_ptr,
922             $version_ptr,
923             $f_index_ptr,
924             $articles_ptr,
925             $unit,
926             $title,
927             $copyright,
928             $sindex_total,
929             $sindex_pos,
930             $version,
931             $embedded_offset,
932             $embedded_total,
933             );
934              
935 1         4 my $infile = $class->{ infile };
936            
937 1 50       51 unless ( sysopen ( IF, $infile, O_RDONLY ) ) {
938 0         0 prerror "Unable to open file '$infile':$!";
939 0         0 return 0;
940             }
941              
942 1 50       16 unless ( sysread ( IF, $hdr, SDICT_HEADER_SIZE, 0 ) ) {
943 0         0 prerror "Unable to sysread from file '$infile':$!";
944 0         0 return 0;
945             }
946              
947 1         6 $class->{ infile_handler } = *IF;
948              
949 1 50       7 if ( substr ( $hdr, 0, 4 ) ne SDICT_SIG ) {
950 0         0 prerror "Wrong signature file '$infile':$!";
951 0         0 return 0;
952             }
953              
954 1         4 $w_lang = substr ( $hdr, $W_LANG_POS, 3 );
955 1         4 $a_lang = substr ( $hdr, $A_LANG_POS, 3 );
956              
957 1         5 $w_lang =~ s|\x0||g;
958 1         3 $a_lang =~ s|\x0||g;
959              
960 1         4 $compr = substr ( $hdr, $COMPRESSOR_POS, 1 );
961              
962 1         4 my $co = unpack ( "C", $compr );
963 1         3 my $cot = $co;
964 1         2 $cot &= hex ( "xf0" );
965 1         3 $cot >>= 4;
966              
967 1         3 $class->{ slevels } = $cot ;
968              
969 1         2 $cot = $co;
970 1         2 $cot &= hex ( "x0f" );
971 1         4 $cot |= hex ( "x30" );
972              
973 1         3 $compr = pack ( "C" , $cot );
974              
975 1 50       6 if ( $compr eq '0' ) {
    50          
    0          
976 0         0 $compr_method = COMPRESSOR_NONE;
977             }
978             elsif ( $compr eq '1' ) {
979 1         3 $compr_method = COMPRESSOR_GZIP;
980             }
981             elsif ( $compr eq '2' ) {
982 0         0 $compr_method = COMPRESSOR_BZIP2;
983             }
984             else {
985 0         0 prerror "Wrong compression type '$compr'";
986 0         0 return 0;
987             }
988              
989 1         3 $class->{ compressor } = $compr_method;
990              
991 1 50       6 if ( $compr_method eq COMPRESSOR_GZIP ) {
    0          
992 1     1   121 eval 'use Compress::Zlib';
  1         1356  
  1         61422  
  1         311  
993 1 50       9 if ( $@ ) {
994 0         0 prerror "Unable to load compression module 'Compress::Zlib' $@";
995 0         0 return 0;
996             }
997             }
998             elsif ( $compr_method eq COMPRESSOR_BZIP2 ) {
999 0         0 eval 'use Compress::Bzip2';
1000 0 0       0 if ( $@ ) {
1001 0         0 prerror "Unable to load compression module 'Compress::Bzip2' $@";
1002 0         0 return 0;
1003             }
1004             }
1005              
1006 1         10 $tot_words = unpack ( "L", substr ( $hdr, $WORDS_TOT_PTR_POS, 4 ) );
1007 1         3 $title_ptr = unpack ( "L", substr ( $hdr, $TITLE_PTR_POS, 4 ) );
1008 1         4 $copyr_ptr = unpack ( "L", substr ( $hdr, $COPYRIGHT_PTR_POS, 4 ) );
1009 1         4 $f_index_ptr = unpack ( "L", substr ( $hdr, $FINDEX_PTR_POS, 4 ) );
1010 1         3 $articles_ptr = unpack ( "L", substr ( $hdr, $ARTICLES_PTR_POS, 4 ) );
1011 1         4 $sindex_total = unpack ( "L", substr ( $hdr, $SINDEX_TOT_PTR_POS, 4 ) );
1012 1         6 $sindex_pos = unpack ( "L", substr ( $hdr, $SINDEX_PTR_POS, 4 ) );
1013 1         3 $version_ptr = unpack ( "L", substr ( $hdr, $VERSION_PTR_POS, 4 ) );
1014              
1015 1         6 $title = read_unit ( $class, $title_ptr );
1016 1 50       4 unless ( $title ) {
1017 0         0 prerror 'Unable to read title';
1018 0         0 return 0;
1019             }
1020              
1021 1         9 $copyright = read_unit ( $class, $copyr_ptr );
1022 1 50       6 unless ( $copyright ) {
1023 0         0 prerror 'Unable to read copyright';
1024 0         0 return 0;
1025             }
1026              
1027 1         5 $version = read_unit ( $class, $version_ptr );
1028 1 50       6 if ( $version eq q{} ) {
1029 0         0 prerror 'Unable to read version';
1030 0         0 return 0;
1031             }
1032              
1033 1         4 $class->{ f_index_pos } = $f_index_ptr;
1034 1         4 $class->{ articles_pos } = $articles_ptr;
1035              
1036 1         5 prinfo 'Dictionary information:';
1037 1         5 prinfo " Title: '$title'";
1038 1         38 prinfo " Copyright: '$copyright'";
1039 1         6 prinfo " Version: '$version'";
1040 1         6 prinfo " Langs: $w_lang/$a_lang";
1041 1         5 prinfo " Words: $tot_words";
1042 1         5 prinfo " Short index: $sindex_total";
1043 1         6 prinfo " Compression: $compr_method";
1044 1         4 prinfo ' ';
1045 1         8 prinfo " Short index offset: ", sprintf ( "0x%x", $sindex_pos );
1046 1         7 prinfo " Full index offset : ", sprintf ( "0x%x", $f_index_ptr );
1047 1         7 prinfo " Articles offset : ", sprintf ( "0x%x", $articles_ptr );
1048 1         6 prinfo ' ';
1049              
1050 1         5 $class->{ header }->{ title } = $title;
1051 1         3 $class->{ header }->{ copyright } = $copyright;
1052 1         2 $class->{ header }->{ version } = $version;
1053 1         4 $class->{ header }->{ w_lang } = $w_lang;
1054 1         4 $class->{ header }->{ a_lang } = $a_lang;
1055 1         3 $class->{ header }->{ words_total } = $tot_words;
1056 1         12 $class->{ header }->{ sindex_total } = $sindex_total;
1057 1         4 $class->{ header }->{ sindex_ptr } = $sindex_pos;
1058 1         4 $class->{ header }->{ f_index_pos } = $f_index_ptr;
1059 1         2 $class->{ header }->{ articles_pos } = $articles_ptr;
1060 1         3 $class->{ header }->{ dct_v2 } = 0;
1061              
1062              
1063 1 50       8 if ( unpack ( "L", substr ( $hdr, $HDR2_SIG_POS, 4 ) ) == SDICT_HDR2_SIG )
1064             {
1065 0         0 prinfo 'Version 2 signature found';
1066              
1067 0         0 $embedded_offset = unpack ( "L", substr ( $hdr, $BIN1_PTR_POS, 4 ) );
1068              
1069 0 0       0 unless ( sysseek ( IF, $embedded_offset, 0 ) ) {
1070 0         0 prerror "Seek error: $!";
1071 0         0 return 1;
1072             }
1073              
1074 0 0       0 unless ( sysread ( IF, $embedded_total, 4, 0 ) ) {
1075 0         0 prerror "Unable to sysread from file '$infile':$!";
1076 0         0 return 1;
1077             }
1078 0         0 $embedded_total = unpack ( "L", substr ( $embedded_total, 0, 4 ) );
1079              
1080 0         0 prinfo " Embedded BIN-1 offset: ", sprintf ( "0x%x", $embedded_offset ) ;
1081 0         0 prinfo " Embedded BIN-1 total : $embedded_total";
1082              
1083 0         0 $class->{ header }->{ dct_v2 } = 1;
1084 0         0 $class->{ header }->{ embedded_offset } = $embedded_offset;
1085 0         0 $class->{ header }->{ embedded_total } = $embedded_total;
1086             }
1087              
1088 1         7 return 1;
1089             }
1090              
1091              
1092             sub read_full_index ($) {
1093 0     0 0 0 my $class = shift;
1094 0         0 my %words_hash = ();
1095 0         0 my @words_list = ();
1096 0         0 my $file = $class->{ infile_handler };
1097 0         0 my $fpos = $class->{ f_index_pos };
1098 0         0 my $hdr = q{};
1099             my (
1100 0         0 $next,
1101             $aptr,
1102             $wlen,
1103             $word
1104             );
1105              
1106 0 0       0 unless ( sysseek ( $file, $fpos, 0 ) ) {
1107 0         0 prerror "Seek error: $!";
1108 0         0 exit 1;
1109             }
1110              
1111             #for( my $i=0; $i < $class->{ header }->{ words_total } ; $i++) {
1112 0         0 for ( my $i=0; $i < $class->{ header }->{ words_total } * 2; $i++) {
1113              
1114 0 0       0 unless (sysread ($file, $hdr, 8, 0)) {
1115 0         0 prerror "Sysread error: $!";
1116 0         0 exit 1;
1117             }
1118              
1119 0         0 $next = unpack ( "S", substr ( $hdr, 0, 2 ) );
1120 0         0 $aptr = unpack ( "L", substr ( $hdr, 4, 4 ) );
1121              
1122 0         0 $wlen = $next - 4 - 2 - 2;
1123              
1124 0 0       0 if ( $next == 0 ) {
1125 0         0 prinfo 'Last word found';
1126 0         0 last;
1127             }
1128              
1129 0 0       0 if ( $wlen < 0 ) {
1130 0         0 prerror 'File format error';
1131 0         0 exit 1;
1132             }
1133              
1134 0 0       0 unless ( sysread ( $file, $word, $wlen, 0 ) ) {
1135 0         0 prerror "Sysread error: $!";
1136 0         0 exit 1;
1137             }
1138              
1139 0         0 push @words_list, $word;
1140 0         0 $words_hash{ $word } = $aptr;
1141             }
1142              
1143 0         0 $class->{ words_list } = \@words_list;
1144 0         0 $class->{ words_hash } = \%words_hash;
1145             }
1146              
1147              
1148             sub read_short_index ($) {
1149 0     0 0 0 my $class = shift;
1150 0         0 my $file = $class->{ infile_handler };
1151 0         0 my $sindex_len = $class->{ header }->{ sindex_total } * SINDEX_ITEM_LEN;
1152 0         0 my $sindex = q{};
1153 0         0 my $sindex_d = q{};
1154             my (
1155 0         0 $sword_u,
1156             $word_ptr,
1157             $fiunit,
1158             $word,
1159             $i
1160             );
1161 0         0 my %sindex_words = ();
1162 0         0 my %temp_index = ();
1163              
1164 0 0       0 unless ( sysseek ( $file, $class->{ header }->{ sindex_ptr }, 0 ) ) {
1165 0         0 prerror "Seek error: $!";
1166 0         0 exit 1;
1167             }
1168              
1169 0 0       0 unless ( sysread ( $file, $sindex, $sindex_len, 0 ) ) {
1170 0         0 prerror "Sysread error: $!";
1171 0         0 return q{};
1172             }
1173            
1174 0 0       0 if ( $class->{ compressor } eq COMPRESSOR_NONE ) {
    0          
    0          
1175 0         0 prinfo 'No decompression needed';
1176 0         0 $sindex_d = $sindex;
1177              
1178             }
1179             elsif ( $class->{ compressor } eq COMPRESSOR_GZIP ) {
1180 0         0 prinfo 'Decompressing short index using gzip';
1181 0         0 $sindex_d = uncompress ( $sindex, GZIP_COMPRESSION_LEVEL );
1182              
1183 0 0       0 unless ( $sindex_d ) {
1184 0         0 prerror ("Decompression failed");
1185 0         0 exit 1;
1186             }
1187              
1188             }
1189             elsif ( $class->{ compressor } eq COMPRESSOR_BZIP2 ) {
1190 0         0 prinfo 'Decompressing short index using bgzip2';
1191 0         0 $sindex_d = Compress::Bzip2::uncompress ( $sindex, GZIP_COMPRESSION_LEVEL );
1192              
1193 0 0       0 unless ( $sindex_d ) {
1194 0         0 prerror ("Decompression failed");
1195 0         0 exit 1;
1196             }
1197             }
1198             else {
1199 0         0 prerror 'Wrong compression';
1200 0         0 exit 1;
1201             }
1202              
1203 0         0 $i = 0;
1204 0         0 for ( @{ $class->{ words_list } } ) {
  0         0  
1205 0         0 $temp_index{ $_ } = $i++;
1206             }
1207              
1208 0         0 for ( $i=0; $i < $class->{ header }->{ sindex_total }; $i++ ) {
1209 0         0 my $sword = substr (
1210             $sindex_d,
1211             $i * SINDEX_ITEM_LEN,
1212             SDICT_SHORT_NDX_LEN * 4
1213             );
1214              
1215 0         0 from_to ( $sword, "UTF-32LE", "utf8" );
1216 0         0 $sword_u = $sword;
1217              
1218 0         0 $sword_u =~ s|\x0||g;
1219              
1220 0         0 $word_ptr = unpack ( "L", substr ( $sindex_d,
1221             $i * SINDEX_ITEM_LEN + SDICT_SHORT_NDX_LEN * 4,
1222             4
1223             )
1224             );
1225              
1226 0 0       0 unless ( sysseek (
1227             $file,
1228             $class->{ header }->{ f_index_pos } + $word_ptr,
1229             0
1230             )
1231             ) {
1232 0         0 prerror "Seek error: $!";
1233 0         0 exit 1;
1234             }
1235              
1236 0 0       0 unless ( sysread ($file, $fiunit, 2+2+4, 0 ) ) {
1237 0         0 prerror "Sysread error: $!";
1238 0         0 return q{};
1239             }
1240              
1241 0         0 my $len = unpack ( "S", substr( $fiunit, 0, 2 ) )
1242             - 4 - 2 - 2;
1243              
1244 0 0       0 unless ( sysread ( $file, $word, $len, 0 ) ) {
1245 0         0 prerror "Sysread error: $!";
1246 0         0 return q{};
1247             }
1248              
1249 0         0 $sindex_words{ $sword_u } = $temp_index{ $word };
1250             }
1251              
1252 0         0 $class->{ sindex_hash } = \%sindex_words;
1253              
1254             # for (keys %sindex_words) { $_ = decode ("utf8", $_); print ">$_<\n"; } die;
1255             # print Dumper $class; die;
1256              
1257 0         0 return 1;
1258             }
1259              
1260              
1261             sub dump_all_words ($) {
1262 0     0 0 0 my $class = shift;
1263             my (
1264 0         0 $word,
1265             $fpos,
1266             $art
1267             );
1268 0         0 my $infile = $class->{ infile_handler };
1269 0         0 my $outfile = $class->{ outfile_handler };
1270 0         0 my $sep = SDICT_SOURCE_FILE_SEP_O;
1271              
1272 0         0 for $word ( @{ $class->{ words_list } } ) {
  0         0  
1273              
1274 0         0 $fpos = $class->{ words_hash }->{ $word } + $class->{ articles_pos };
1275              
1276 0         0 $art = $class->read_unit ( $fpos );
1277              
1278 0 0       0 if ( $art eq q{} ) {
1279 0         0 prerror "Unable to read article for word '$word'";
1280 0         0 exit 1;
1281             }
1282              
1283 0         0 print $outfile $word;
1284 0         0 print $outfile $sep;
1285 0         0 print $outfile $art;
1286 0         0 print $outfile "\n";
1287             }
1288              
1289 0         0 print $outfile "#\n# End of articles\n#\n";
1290              
1291 0         0 return 1;
1292             }
1293              
1294              
1295             sub read_unit ($$) {
1296 4     4 0 12 my ( $class, $fpos ) = @_[0,1];
1297 4         19 my $file = $class->{ infile_handler };
1298 4         8 my $unit = q{};
1299 4         7 my $val = q{};
1300              
1301 4 50       33 unless ( sysseek ( $file, $fpos, 0 ) ) {
1302 0         0 prerror "Seek error: $!";
1303 0         0 return q{};
1304             }
1305              
1306 4 50       40 unless ( sysread ( $file, $unit, 4, 0 ) ) {
1307 0         0 prerror "Sysread error: $!";
1308 0         0 return q{};
1309             }
1310              
1311 4 50       41 unless ( sysread (
1312             $file,
1313             $val,
1314             unpack ("L", $unit),
1315             0
1316             )
1317             ) {
1318 0         0 prerror "Sysread error: $!";
1319 0         0 return q{};
1320             }
1321              
1322 4         19 return ( decompress_unit ( $class, $unit . $val ) );
1323             }
1324              
1325              
1326             sub analyze ($) {
1327 0     0 0 0 my $class = shift;
1328              
1329 0         0 $class->{ outfile } = "temp-$$";
1330              
1331 0         0 prinfo 'Retrieving headers';
1332 0 0       0 exit 1 unless $class->get_infile_headers;
1333              
1334 0         0 prinfo 'Making header';
1335 0 0       0 exit 1 unless $class->create_header;
1336              
1337 0         0 prinfo 'Retrieving articles and making words hash';
1338 0 0       0 exit 1 unless $class->make_articles;
1339              
1340 0         0 prinfo 'Making full index';
1341 0 0       0 exit 1 unless $class->make_full_index;
1342              
1343 0         0 my ( $j, $mm );
1344 0         0 my %hh = ();
1345              
1346 0 0 0     0 if (! exists $class->{ analyze_max } ||
      0        
1347             $class->{ analyze_max } < 3 ||
1348             $class->{ analyze_max } > 15
1349             ) {
1350              
1351 0         0 $mm = 3;
1352             }
1353             else {
1354 0         0 $mm = $class->{ analyze_max };
1355             }
1356              
1357              
1358 0         0 for ( $j=3; $j <=$mm; $j++ ) {
1359 0         0 $class->{ slevels } = $j;
1360              
1361 0         0 prinfo "Making short index for $j";
1362 0 0       0 exit 1 unless $class->make_short_index;
1363              
1364 0         0 my $ucs = $class->{ temp_si_file_size_unc };
1365 0         0 my $ccs = $class->{ temp_si_file_size };
1366              
1367 0         0 prinfo "Analyzing gap for $j";
1368 0         0 my $m = $class->analyze_gaps;
1369              
1370 0         0 $hh{$j} = "$ucs/$ccs $m";
1371             }
1372              
1373 0         0 prinfo 'Cleanups';
1374 0         0 unlink $class->{ outfile };
1375 0 0       0 exit 1 unless $class->cleanups;
1376              
1377 0         0 prinfo q{};
1378 0         0 prinfo q{};
1379 0         0 prinfo '*******************************************************';
1380 0         0 prinfo '*** SUMMARY ***';
1381 0         0 prinfo '*******************************************************';
1382 0         0 prinfo "Dictionary: $class->{ header }->{ title }";
1383 0         0 $_ = scalar ( @{ $class->{ words_list } } );
  0         0  
1384 0         0 prinfo "Words: $_";
1385 0         0 prinfo q{};
1386              
1387 0         0 for (sort { $a<=>$b } keys (%hh) ) {
  0         0  
1388 0         0 prinfo "Sindex for $_ : $hh{$_}";
1389             }
1390 0         0 prinfo q{};
1391 0         0 prinfo '*******************************************************';
1392              
1393 0         0 return 1;
1394             }
1395              
1396              
1397             sub analyze_gaps ($) {
1398 0     0 0 0 my $class = shift;
1399 0         0 my $len = $class->{ slevels };
1400              
1401 0         0 my @words = @{ $class->{ words_list } };
  0         0  
1402 0         0 for (@words) {
1403 0         0 $_ = decode ("utf8", $_);
1404             }
1405              
1406 0         0 my %h = ();
1407              
1408 0         0 for ( @words ) {
1409 0         0 $_ = substr( $_, 0 , $len );
1410 0         0 $h{$_}++;
1411             }
1412              
1413 0         0 my %h2 = reverse %h;
1414              
1415 0         0 my $i = 0;
1416 0         0 my $m = q{[ };
1417 0         0 for ( reverse ( sort { $a <=> $b } keys ( %h2 ) ) ) {
  0         0  
1418 0         0 $m .= "$_/'$h2{$_}'; ";
1419 0 0       0 last if ($i++ >3);
1420             }
1421              
1422 0         0 $m .= ']';
1423              
1424 0         0 prinfo $m;
1425 0         0 return $m;
1426             }
1427              
1428              
1429             sub compile ($) {
1430 0     0 0 0 my $class = shift;
1431              
1432 0         0 prinfo '--- COMPILE ---';
1433              
1434 0 0       0 if ( $class->{ slevels } != 3 ) {
1435 0         0 prinfo 'Use non-standard short index levels value can cause incompatibility problems!';
1436 0 0 0     0 if ( -t STDIN && -t STDOUT ) {
1437             {
1438 0         0 local $|=1;
  0         0  
1439 0         0 for ( my $i=0; $i<1; $i++ ) {
1440 0         0 print "\a";
1441 0         0 sleep 1;
1442             }
1443             }
1444             }
1445             }
1446              
1447 0         0 prinfo '--- Retrieving headers ---';
1448 0 0       0 exit 1 unless $class->get_infile_headers;
1449              
1450 0         0 prinfo '--- Making header ---';
1451 0 0       0 exit 1 unless $class->create_header;
1452              
1453 0         0 prinfo '--- Retrieving articles and making words hash ---';
1454 0 0       0 exit 1 unless $class->make_articles;
1455              
1456 0         0 prinfo '--- Making full index ---';
1457 0 0       0 exit 1 unless $class->make_full_index;
1458              
1459 0         0 prinfo '--- Making short index ---';
1460 0 0       0 exit 1 unless $class->make_short_index;
1461              
1462 0         0 prinfo '--- Tunning header ---';
1463 0 0       0 exit 1 unless $class->correct_header;
1464              
1465 0         0 prinfo '--- Joining files ---';
1466 0 0       0 exit 1 unless $class->join_files;
1467              
1468 0         0 prinfo '--- Cleanups ---';
1469 0 0       0 exit 1 unless $class->cleanups;
1470              
1471 0         0 return 1;
1472             }
1473              
1474              
1475             sub get_infile_headers ($) {
1476 0     0 0 0 my $class = shift;
1477 0         0 my %h =();
1478 0         0 my $fl = 0;
1479 0         0 my $file = $class->{ infile };
1480              
1481 0 0       0 unless ( open F, "< $file" ) {
1482 0         0 prerror "Unable to open input file '$file': $!";
1483 0         0 return 0;
1484             }
1485              
1486 0         0 while () {
1487 0         0 chomp;
1488 0         0 s/\r$//;
1489 0 0       0 next if /^\#/;
1490 0 0       0 next if /^\s*$/;
1491 0 0       0 if (/^
/) { $fl=1; next; }
  0         0  
  0         0  
1492 0 0       0 last if (/^<\/header>/);
1493 0 0       0 next unless $fl;
1494 0 0       0 next unless /\s=\s/;
1495 0         0 my ($p,$v) = ( split ( /\s=\s/, $_, 2 ) )[0,1];
1496 0         0 $p=~s|^\s+||; $p=~s|\s+$||;
  0         0  
1497 0         0 $v=~s|^\s+||; $v=~s|\s+$||;
  0         0  
1498 0 0 0     0 next if ( ($p eq q{}) || ($v eq q{}) );
1499 0         0 $h{$p} = $v;
1500             }
1501              
1502 0         0 close F;
1503              
1504 0 0       0 unless (defined($h{'title'})) {
1505 0         0 prerror "Missing keyword 'title' in file '$file'";
1506 0         0 return 0;
1507             }
1508              
1509 0 0       0 unless (defined($h{'copyright'})) {
1510 0         0 prerror "Missing keyword 'copyright' in file '$file'";
1511 0         0 return 0;
1512             }
1513              
1514 0 0       0 unless (defined($h{'w_lang'})) {
1515 0         0 prerror "Missing keyword 'w_lang' in file '$file'";
1516 0         0 return 0;
1517             }
1518              
1519 0 0       0 unless (defined($h{'a_lang'})) {
1520 0         0 prerror "Missing keyword 'a_lang' in file '$file'";
1521 0         0 return 0;
1522             }
1523              
1524 0 0       0 unless (defined($h{'version'})) {
1525 0         0 prerror "Missing keyword 'version' in file '$file'";
1526 0         0 return 0;
1527             }
1528              
1529              
1530 0         0 $h{'w_lang'} = substr( $h{'w_lang'}, 0, 3 );
1531 0         0 $h{'a_lang'} = substr( $h{'a_lang'}, 0, 3 );
1532              
1533 0 0       0 if ( exists ( $h{ 'charset' } ) ) {
1534 0 0       0 unless ( grep /^$h{ 'charset' }$/, Encode->encodings (":all") ) {
1535 0         0 prerror "Wrong charset '$h{ 'charset' }'";
1536 0         0 print_available_charsets ();
1537 0         0 return 0;
1538             }
1539 0 0       0 if ( $h{ 'charset' } eq 'utf8' ) {
1540 0         0 delete $h{ 'charset' };
1541             }
1542             }
1543              
1544 0 0       0 if ( exists ( $h{ 'charset' } ) ) {
1545 0         0 from_to ( $h{ 'version' }, $h{ 'charset' }, "utf8" );
1546 0         0 from_to ( $h{ 'copyright' }, $h{ 'charset' }, "utf8" );
1547 0         0 from_to ( $h{ 'title' }, $h{ 'charset' }, "utf8" );
1548             }
1549              
1550 0         0 $class->{ header }=\%h;
1551 0         0 return 1;
1552             }
1553              
1554              
1555             sub print_available_charsets {
1556 0     0 0 0 prinfo 'Available charsets are:' ;
1557 0         0 @_ = sort ( Encode->encodings (":all") );
1558 0         0 prinfo @_;
1559             }
1560              
1561              
1562             sub create_header ($) {
1563 0     0 0 0 my $class=shift;
1564              
1565             my (
1566 0         0 $word_amount,
1567             $title_ptr,
1568             $copyright_ptr,
1569             $version_ptr,
1570             $short_ndx_ptr,
1571             $full_ndx_ptr,
1572             $articles_ptr,
1573             $sindex_amount
1574             );
1575              
1576 0         0 $word_amount = $title_ptr = $copyright_ptr = $short_ndx_ptr =
1577             $full_ndx_ptr = $articles_ptr = $sindex_amount = 0;
1578              
1579 0         0 my $title_unit = create_unit( $class, $class->{ header }->{ title } );
1580 0         0 my $copyright_unit = create_unit( $class, $class->{ header }->{ copyright } );
1581 0         0 my $version_unit = create_unit( $class, $class->{ header }->{ version } );
1582              
1583 0         0 my $w_lang = substr ( $class->{ header }->{ w_lang }, 0, 2 ) . pack ( "c", 0 );
1584 0         0 my $a_lang = substr ( $class->{ header }->{ a_lang }, 0, 2 ) . pack ( "c", 0 );
1585              
1586 0         0 $title_ptr = SDICT_HEADER_SIZE;
1587 0         0 $copyright_ptr = $title_ptr + length( $title_unit );
1588 0         0 $version_ptr = $copyright_ptr + length( $copyright_unit );
1589 0         0 $short_ndx_ptr = $version_ptr + length( $version_unit );
1590              
1591 0         0 my $co = hex ( $COMPRESSION{ $class->{ compressor } } ) & 0x0f;
1592 0         0 my $sl = $class->{ slevels };
1593              
1594 0         0 $sl = ( ($sl & 0x0f) << 4 ) & 0xf0;
1595              
1596 0         0 $sl = pack ( "C", ( $sl | $co ) );
1597              
1598 0         0 my $hdr2_sig_pre = SDICT_HDR2_SIG + 1; # wrong at the moment, correct later
1599              
1600 0         0 my $header = SDICT_SIG . $w_lang . $a_lang . $sl .
1601             pack ("L9CL", $word_amount, $sindex_amount, $title_ptr, $copyright_ptr,
1602             $version_ptr, $short_ndx_ptr, $full_ndx_ptr, $articles_ptr,
1603             $hdr2_sig_pre, 9, hex ("0xffffffff") );
1604              
1605             $class->{ header_file_size } =
1606 0         0 length ( $header ) +
1607             length ( $title_unit ) +
1608             length ( $copyright_unit ) +
1609             length ( $version_unit );
1610              
1611 0         0 my $oufile = $class->{ outfile };
1612              
1613 0         0 prinfo "Writing header into file '$oufile'";
1614              
1615 0 0       0 unless ( open ( F, ">$oufile" ) ) {
1616 0         0 prerror "Unable to create file '$oufile': $!";
1617 0         0 exit 1;
1618             }
1619              
1620 0         0 binmode F;
1621              
1622 0         0 print F $header;
1623              
1624 0         0 print F $title_unit;
1625 0         0 print F $copyright_unit;
1626 0         0 print F $version_unit;
1627 0         0 close F;
1628 0         0 return 1;
1629             }
1630              
1631              
1632             sub correct_header ($) {
1633 0     0 0 0 my $class = shift;
1634 0         0 my $val = 0;
1635              
1636 0 0       0 unless ( sysopen ( HDR, $class->{ outfile }, O_RDWR ) ) {
1637 0         0 prerror "Unable to open file '", $class->{ outfile }, "':$!";
1638 0         0 exit 1;
1639             }
1640              
1641 0 0       0 unless ( sysseek( HDR, $WORDS_TOT_PTR_POS, 0 ) ) {
1642 0         0 prerror "Seek error: $!";
1643 0         0 exit 1;
1644             }
1645             else {
1646 0         0 $val = pack ( "L", $class->{ words_total } );
1647 0         0 syswrite (HDR, $val);
1648             }
1649              
1650 0 0       0 unless ( sysseek( HDR, $SINDEX_TOT_PTR_POS, 0 ) ) {
1651 0         0 prerror "Seek error: $!";
1652 0         0 exit 1;
1653             }
1654             else {
1655 0         0 $val = pack ( "L", $class->{ sindex_total } );
1656 0         0 syswrite (HDR, $val);
1657             }
1658              
1659 0 0       0 unless ( sysseek ( HDR, $SINDEX_PTR_POS, 0 ) ) {
1660 0         0 prerror "Seek error: $!";
1661 0         0 exit 1;
1662             }
1663             else {
1664 0         0 $val = pack ( "L", $class->{ header_file_size } );
1665 0         0 syswrite ( HDR, $val );
1666             }
1667              
1668 0 0       0 unless ( sysseek ( HDR, $FINDEX_PTR_POS, 0 ) ) {
1669 0         0 prerror "Seek error: $!";
1670 0         0 exit 1;
1671             }
1672             else {
1673             $val = pack (
1674             "L",
1675             $class->{ header_file_size } + $class->{ temp_si_file_size }
1676 0         0 );
1677              
1678 0         0 syswrite ( HDR, $val );
1679             }
1680              
1681 0 0       0 unless ( sysseek ( HDR, $ARTICLES_PTR_POS, 0 ) ) {
1682 0         0 prerror "Seek error: $!";
1683 0         0 exit 1;
1684             }
1685             else {
1686             $val = pack (
1687             "L",
1688             $class->{ header_file_size } +
1689             $class->{ temp_si_file_size } +
1690             $class->{ temp_fi_file_size }
1691 0         0 );
1692 0         0 syswrite ( HDR, $val );
1693             }
1694              
1695              
1696              
1697 0 0 0     0 if ( $class->{ parse_embedded } && $class->{ embedded_total } )
1698             {
1699 0         0 prinfo 'Adding bin1 storage';
1700              
1701 0 0       0 unless ( sysseek ( HDR, $HDR2_SIG_POS, 0 ) ) {
1702 0         0 prerror "Seek error: $!";
1703 0         0 exit 1;
1704             }
1705              
1706              
1707             $val = pack ("LCL",
1708             SDICT_HDR2_SIG,
1709             1,
1710             $class->{ header_file_size } +
1711             $class->{ temp_si_file_size } +
1712             $class->{ temp_fi_file_size } +
1713             $class->{ temp_ar_file_size }
1714 0         0 );
1715 0         0 syswrite ( HDR, $val );
1716              
1717             }
1718              
1719              
1720 0         0 close HDR;
1721 0         0 return 1;
1722              
1723             }
1724              
1725              
1726             sub make_articles ($) {
1727 0     0 0 0 my $class = shift;
1728 0         0 my %words_hash = ();
1729 0         0 my %words_dups = ();
1730 0         0 my @words_list = ();
1731 0         0 my $oufile = $class->{ outfile };
1732 0         0 my $articles_total = 0;
1733 0         0 my $lines = 0;
1734 0         0 my $lines_skp = 0;
1735 0         0 my $lines_passed = 0;
1736 0         0 my $aliases = 0;
1737              
1738             my (
1739 0         0 $line,
1740             $word,
1741             $art,
1742             $alword,
1743             $aunit,
1744             %h_img,
1745             %h_snd,
1746             );
1747              
1748 0         0 my $sep = SDICT_SOURCE_FILE_SEP;
1749 0         0 my $art_ptr = 0;
1750              
1751              
1752 0 0 0     0 if ( $class->{ lowercasealias } || $class->{ forcetolowercase } ) {
1753 0         0 eval 'use SdictUtils';
1754 0 0       0 if ( $@ ) {
1755 0         0 prerror "Unable to load module 'SdictUtils' $@";
1756 0         0 exit 1;
1757             }
1758             }
1759              
1760              
1761 0         0 my $temp_afile = $oufile . '-tmp1-' . $$;
1762 0         0 prinfo "Creating temporary file '$temp_afile'";
1763 0 0       0 unless ( open ( DF, ">$temp_afile" ) ) {
1764 0         0 prerror "Unable create file '$temp_afile':$!";
1765 0         0 return 0;
1766             }
1767 0         0 binmode DF;
1768 0         0 $class->{ temp_afile } = $temp_afile;
1769              
1770              
1771 0         0 my $temp_bin1_ndx = $oufile . '-tmp4-' . $$; # for bin1 index storage
1772 0 0       0 if ( $class->{ parse_embedded } )
1773             {
1774 0         0 prinfo "Creating temporary file '$temp_bin1_ndx'";
1775 0 0       0 unless ( open ( BFI, ">$temp_bin1_ndx" ) ) {
1776 0         0 prerror "Unable create file '$temp_bin1_ndx':$!";
1777 0         0 return 0;
1778             }
1779 0         0 binmode BFI;
1780 0         0 $class->{ temp_bin1_ndx_file } = $temp_bin1_ndx;
1781             }
1782              
1783              
1784 0         0 my $temp_bin1 = $oufile . '-tmp5-' . $$; # for bin1 storage
1785 0 0       0 if ( $class->{ parse_embedded } )
1786             {
1787 0         0 prinfo "Creating temporary file '$temp_bin1'";
1788 0 0       0 unless ( open ( BF, ">$temp_bin1" ) ) {
1789 0         0 prerror "Unable create file '$temp_bin1':$!";
1790 0         0 return 0;
1791             }
1792 0         0 binmode BF;
1793 0         0 $class->{ temp_bin1_file } = $temp_bin1;
1794             }
1795              
1796              
1797 0         0 my $infile = $class->{ infile };
1798 0         0 prinfo "Parsing source file '$infile'";
1799              
1800              
1801 0 0       0 unless ( open ( SF, "< $infile" ) ) {
1802 0         0 prerror "Unable open file '$infile': $!";
1803 0         0 return 0;
1804             }
1805              
1806              
1807 0         0 while () {
1808 0         0 $lines++;
1809 0         0 chomp;
1810 0         0 s/\r$//;
1811 0 0       0 next if /^\#/ ;
1812 0 0       0 next if /^\s*$/ ;
1813 0 0       0 last if /^<\/header>/ ;
1814             }
1815              
1816 0         0 while ()
1817             {
1818 0         0 $lines++;
1819 0         0 chomp;
1820 0         0 s/\r$//;
1821 0 0       0 next if /^\#/ ;
1822 0 0       0 next if /^\s*$/ ;
1823 0         0 $line = $_;
1824 0 0       0 next unless ( /$sep/ );
1825 0         0 ( $word, $art ) = ( split ( /$sep/, $line,2 ) )[0,1];
1826              
1827 0 0       0 if ( exists ( $class->{ header }->{ charset } ) ) {
1828 0         0 from_to ( $word, $class->{ header }->{ charset }, "utf8" );
1829 0         0 from_to ( $art, $class->{ header }->{ charset }, "utf8" );
1830             }
1831              
1832 0 0 0     0 if ( ( $word eq q{} ) || ( $art eq q{} ) ) {
1833 0         0 prerror "Skipped wrong line at $lines '$line'";
1834 0         0 $lines_skp++;
1835 0         0 next;
1836             }
1837              
1838 0 0       0 if ( length ( $word ) > SDICT_WORD_MAX_SIZE) {
1839 0         0 $word = substr ( $word, 0, SDICT_WORD_MAX_SIZE );
1840 0         0 print "Truncated word at line $lines\n";
1841             }
1842              
1843 0 0       0 if ( length ( $art ) > SDICT_ART_MAX_SIZE ) {
1844 0         0 $art = substr ($art, 0, SDICT_ART_MAX_SIZE );
1845 0         0 print "Truncated art at line $lines\n";
1846             }
1847              
1848 0         0 $lines_passed++;
1849              
1850             #
1851             # Handle images if any
1852             #
1853 0 0       0 if ( $class->{ parse_embedded } )
1854             {
1855             #
1856             # Images
1857             #
1858 0         0 my $image_unit = q{};
1859 0         0 my $image_unit_len = 0;
1860              
1861 0         0 while ( $art =~ m||gi )
1862             {
1863 0         0 my $emb_sur_num = $class->{ embedded_cur_num } ;
1864 0         0 my $img_filename = $class->{ images_dir } . $1;
1865              
1866 0 0       0 unless ( $img_filename ) {
1867 0         0 prerror "Bad image filename '$img_filename'" ;
1868 0         0 return 0;
1869             }
1870              
1871 0 0       0 if ( exists $h_img{ $img_filename } ) {
1872 0         0 prinfo "Image $img_filename already in storage, num= $h_img{ $img_filename }";
1873              
1874 0         0 $art =~ s|||i ;
1875             }
1876             else {
1877 0         0 $h_img{ $img_filename } = $emb_sur_num ;
1878 0         0 $art =~ s|||i ;
1879              
1880 0         0 $image_unit = create_image_unit ( $img_filename, $class->{ try_djvu_first } );
1881              
1882 0         0 $image_unit_len = length ( $image_unit );
1883 0 0       0 unless ($image_unit_len)
1884             {
1885 0         0 warn "Cannot create image unit";
1886 0         0 return 0;
1887             }
1888 0         0 $_ = $class->{ embedded_cur_offset };
1889 0         0 prinfo "Addind image, unit size= $image_unit_len, offset= $_";
1890              
1891 0         0 push ( @{$class->{ embedded_offsets }}, $class->{ embedded_cur_offset } );
  0         0  
1892 0         0 print BF $image_unit;
1893              
1894 0         0 $class->{ embedded_total }++;
1895 0         0 $class->{ embedded_cur_num }++;
1896 0         0 $class->{ embedded_cur_offset } += $image_unit_len;
1897             }
1898             }
1899 0         0 $art =~ s|
1900              
1901              
1902             #
1903             # Sound samples
1904             #
1905 0         0 my $sound_unit = q{};
1906 0         0 my $sound_unit_len = 0;
1907              
1908 0         0 while ( $art =~ m||gi )
1909             {
1910 0         0 my $emb_sur_num = $class->{ embedded_cur_num } ;
1911 0         0 my $snd_filename = $class->{ sounds_dir } . $1;
1912              
1913 0 0       0 unless ( $snd_filename ) {
1914 0         0 prerror "Bad sound filename '$snd_filename'" ;
1915 0         0 return 0;
1916             }
1917              
1918 0 0       0 if ( exists $h_snd{ $snd_filename } ) {
1919 0         0 prinfo "Sound $snd_filename already in storage, num= $h_snd{ $snd_filename }";
1920              
1921 0         0 $art =~ s|||i ;
1922             }
1923             else {
1924 0         0 $h_snd{ $snd_filename } = $emb_sur_num ;
1925 0         0 $art =~ s|||i ;
1926              
1927 0         0 $sound_unit = create_sound_unit ( $snd_filename );
1928              
1929 0         0 $sound_unit_len = length ( $sound_unit );
1930 0 0       0 unless ($sound_unit_len)
1931             {
1932 0         0 warn "Cannot create sound unit";
1933 0         0 return 0;
1934             }
1935 0         0 $_ = $class->{ embedded_cur_offset };
1936 0         0 prinfo "Addind sound, unit size= $sound_unit_len, offset= $_";
1937              
1938 0         0 push ( @{$class->{ embedded_offsets }}, $class->{ embedded_cur_offset } );
  0         0  
1939 0         0 print BF $sound_unit;
1940              
1941 0         0 $class->{ embedded_total }++;
1942 0         0 $class->{ embedded_cur_num }++;
1943 0         0 $class->{ embedded_cur_offset } += $sound_unit_len;
1944             }
1945             }
1946 0         0 $art =~ s|
1947             }
1948              
1949              
1950             #
1951             # Pack article into unit
1952             #
1953 0         0 $aunit = create_unit ( $class, $art );
1954              
1955              
1956             #
1957             # to lowercase
1958             #
1959 0 0       0 if ( $class->{ forcetolowercase } ) {
1960              
1961 0         0 $word = utf8_lowercase ( decode ( "utf8", $word ) );
1962              
1963 0 0       0 if ( $word eq q{} ) {
1964 0         0 prerror "Unable to lowercase word '$word'";
1965 0         0 return q{} ;
1966             }
1967              
1968 0         0 $word = encode ( "utf8", $word ) ;
1969             }
1970              
1971             #
1972             # Duplicates
1973             #
1974 0 0       0 if ( exists ( $words_hash{ $word } ) ) {
1975 0 0       0 if ( $class->{ disableduplicates } ) {
1976 0         0 prerror "Duplicated word '$word'";
1977 0         0 return {} ;
1978             }
1979              
1980 0         0 $words_dups{ $word }++; # 1 - 2nd, 2 - 3rd and so on...
1981 0         0 my $nname = $words_dups{ $word };
1982 0         0 $nname++;
1983 0         0 $word .= " ($nname)";
1984             }
1985             #
1986             # Store word
1987             #
1988 0         0 push ( @words_list, $word );
1989 0         0 $words_hash{ $word } = $art_ptr;
1990              
1991 0         0 $art_ptr += length ( $aunit );
1992 0         0 print DF $aunit;
1993             # print "L>$line<\n";
1994             }
1995              
1996             #
1997             # Making bin1 indices
1998             #
1999 0 0 0     0 if ( $class->{ parse_embedded } && $class->{ embedded_total } )
2000             {
2001              
2002 0         0 my $emb_tot = $class->{ embedded_total };
2003 0         0 prinfo 'Creating bin1 indices, emb_tot= $emb_tot';
2004 0         0 my $ndx_off = 4 * ( $emb_tot + 1 );
2005 0         0 print BFI pack ( "L", $emb_tot );
2006              
2007 0         0 for my $ndx ( @{$class->{ embedded_offsets }} )
  0         0  
2008             {
2009 0         0 print BFI pack ( "L", $ndx + $ndx_off );
2010             }
2011             }
2012              
2013 0         0 close SF;
2014 0         0 close DF;
2015              
2016 0         0 $class->{ temp_ar_file_size } = ( stat ( $temp_afile ) )[7];
2017              
2018              
2019 0 0       0 if ( $class->{ parse_embedded } )
2020             {
2021 0         0 close BF;
2022 0         0 close BFI;
2023             }
2024              
2025              
2026             # lowercase aliases
2027 0 0       0 if ( $class->{ lowercasealias } ) {
2028 0         0 prinfo "Making lowercase aliases";
2029              
2030 0         0 for my $ww ( keys ( %words_hash ) ) {
2031              
2032 0         0 $alword = utf8_lowercase ( decode ( "utf8", $ww ) );
2033              
2034 0 0       0 if ( $alword ne q{} ) {
2035              
2036 0         0 $alword = encode ( "utf8", $alword ) ;
2037              
2038 0 0 0     0 if ( ( $alword ne $ww ) && ( ! exists ( $words_hash{ $alword } ) ) ) {
2039 0         0 push ( @words_list, $alword );
2040 0         0 $words_hash{ $alword } = $words_hash{ $ww };
2041 0         0 $aliases++;
2042             }
2043             }
2044             }
2045             }
2046             #
2047              
2048              
2049 0         0 prinfo "Lines - total: $lines, skipped:$lines_skp, passed:$lines_passed";
2050              
2051 0 0       0 if ( $class->{ lowercasealias } ) {
2052 0         0 prinfo "Aliases created: $aliases";
2053             }
2054              
2055 0         0 $class->{ words_total } = $lines_passed;
2056 0         0 $class->{ words_list } = \@words_list;
2057 0         0 $class->{ words_hash } = \%words_hash;
2058              
2059              
2060 0 0       0 $class->sort_words_list if ( $class->{ sort } );
2061              
2062 0         0 return 1;
2063             }
2064              
2065              
2066             sub create_sound_unit ($) {
2067 0     0 0 0 my ($file) = @_;
2068 0         0 prinfo "Creating sound unit from file '$file'";
2069              
2070 0         0 my $unit = q{};
2071              
2072 0         0 my $snd_type = get_sound_type ($file);
2073              
2074              
2075 0 0       0 if ( $snd_type == SDICT_SND_MP3 )
2076             {
2077 0         0 prinfo "MP3 sound file, type $snd_type";
2078              
2079 0 0       0 unless (open (SNF, "< $file")) {
2080 0         0 prerror "Cannot open '$file': $!";
2081 0         0 return q{};
2082             }
2083 0         0 binmode SNF;
2084              
2085 0         0 my $raw_sound = q{};
2086             {
2087 0         0 local $/ = undef;
  0         0  
2088 0         0 $raw_sound = ;
2089             }
2090 0         0 close SNF;
2091              
2092              
2093 0         0 my $snd_len = 1 ; # TODO get_sound_length ($file);
2094              
2095 0 0       0 if (! $snd_len ) {
2096 0         0 prerror "cannot get sound length for file '$file'";
2097 0         0 return q{};
2098             }
2099              
2100 0         0 my $sz = length ($raw_sound);
2101 0         0 prinfo "Sound type $snd_type, len= $snd_len sec, size= $sz bytes" ;
2102              
2103 0         0 $unit = pack ("LCS",
2104             $sz + 1 + 2,
2105             $snd_type,
2106             $snd_len ) . $raw_sound;
2107             }
2108             else
2109             {
2110 0         0 prerror "unsupported sound type $snd_type";
2111             }
2112              
2113 0         0 return $unit;
2114             }
2115              
2116              
2117             sub get_sound_type ($) {
2118 0     0 0 0 my $file = $_[0];
2119              
2120 0         0 $file =~ s|.+\.||;
2121 0         0 prinfo "File suffix is '$file'";
2122              
2123              
2124 0 0       0 if ( $file =~ /mp3/i ) {
2125 0         0 return SDICT_SND_MP3;
2126             }
2127 0         0 return 0;
2128             }
2129              
2130              
2131             sub create_image_unit ($) {
2132 0     0 0 0 my ($file, $try_djvu_first) = @_;
2133 0         0 prinfo "Creating image unit from file '$file'";
2134              
2135 0         0 my $unit = q{};
2136              
2137 0         0 my $img_type = get_image_type ($file);
2138              
2139              
2140 0 0 0     0 if ( $try_djvu_first &&
      0        
2141             ( $img_type == SDICT_IMG_PNG ||
2142             $img_type == SDICT_IMG_GIF ||
2143             $img_type == SDICT_IMG_JPEG ) )
2144             {
2145 0         0 my $file2 = $file;
2146 0         0 $file2 =~ s|^(.+)\..+$|$1.djvu|;
2147              
2148 0         0 prinfo "Trying file '$file2' instead of '$file'";
2149              
2150 0 0       0 if (open (IMF, "< $file2")) {
2151 0         0 close IMF;
2152 0         0 prinfo 'Yes, found';
2153 0         0 $file = $file2;
2154 0         0 $img_type = SDICT_IMG_DJVU;
2155             }
2156             else {
2157 0         0 prinfo 'Not found';
2158             }
2159             }
2160              
2161              
2162 0 0 0     0 if ( $img_type == SDICT_IMG_PNG ||
    0 0        
2163             $img_type == SDICT_IMG_GIF ||
2164             $img_type == SDICT_IMG_JPEG )
2165             {
2166 0         0 prinfo "usual image file, type $img_type";
2167              
2168 0 0       0 unless (open (IMF, "< $file")) {
2169 0         0 prerror "Cannot open '$file': $!";
2170 0         0 return q{};
2171             }
2172 0         0 binmode IMF;
2173              
2174 0         0 my $raw_image = q{};
2175             {
2176 0         0 local $/ = undef;
  0         0  
2177 0         0 $raw_image = ;
2178             }
2179 0         0 close IMF;
2180              
2181 0         0 my @img_res = get_image_resolution ($file);
2182              
2183 0 0 0     0 if (! @img_res || ! $img_res[0] || ! $img_res[1] ) {
      0        
2184 0         0 prerror "cannot get resolution for file '$file'";
2185 0         0 return q{};
2186             }
2187 0         0 my $sz = length ($raw_image);
2188 0         0 prinfo "Image type $img_type, res= $img_res[0]x$img_res[1], size= $sz bytes" ;
2189              
2190 0         0 $unit = pack ("LCS2",
2191             $sz + 1 + 2 + 2,
2192             $img_type,
2193             $img_res[0],
2194             $img_res[1] ) . $raw_image;
2195              
2196             }
2197             elsif ( $img_type == SDICT_IMG_DJVU )
2198             {
2199 0         0 prinfo "DJVU image file, looking inside";
2200 0         0 my $djvu = Sdict::Utils::parse_djvu_file ($file);
2201 0 0       0 return $unit unless $djvu;
2202              
2203 0 0 0     0 if ( ! $djvu->{ width } || ! $djvu->{ height } ) {
2204 0         0 prerror "cannot get resolution for file '$file'";
2205 0         0 return $unit;
2206             }
2207              
2208 0         0 my @img_res = ( $djvu->{ width }, $djvu->{ height } );
2209 0         0 my $raw_image = q{};
2210 0         0 my $sz = 0;
2211              
2212 0 0       0 if ( defined ( $djvu->{ bg44 } ) ) {
2213 0         0 $img_type = SDICT_IMG_IW44;
2214 0         0 $raw_image = $djvu->{ bg44 } ;
2215             }
2216              
2217 0 0       0 if ( defined ( $djvu->{ sjbz } ) ) {
2218 0         0 $img_type = SDICT_IMG_JB2;
2219 0         0 $raw_image = $djvu->{ sjbz } ;
2220             }
2221              
2222 0 0       0 if ( $img_type == SDICT_IMG_DJVU ) {
2223 0         0 prerror "cannot get type IW44/JB2";
2224 0         0 return $unit;
2225             }
2226              
2227 0         0 $sz = length ( $raw_image );
2228 0         0 prinfo "Image type $img_type, res= $img_res[0]x$img_res[1], size= $sz bytes" ;
2229              
2230 0         0 $unit = pack ("LCS2",
2231             $sz + 1 + 2 + 2,
2232             $img_type,
2233             $img_res[0],
2234             $img_res[1] ) . $raw_image;
2235             }
2236             else
2237             {
2238 0         0 prerror "unsupported image type $img_type";
2239             }
2240              
2241 0         0 return $unit;
2242             }
2243              
2244              
2245             sub get_image_type ($) {
2246 0     0 0 0 my $file = $_[0];
2247              
2248 0         0 $file =~ s|.+\.||;
2249 0         0 prinfo "File suffix is '$file'";
2250              
2251              
2252 0 0       0 if ( $file =~ /jp.?g/i ) {
2253 0         0 return SDICT_IMG_JPEG;
2254             }
2255              
2256 0 0       0 if ( $file =~ /gif/i ) {
2257 0         0 return SDICT_IMG_GIF;
2258             }
2259              
2260 0 0       0 if ( $file =~ /png/i ) {
2261 0         0 return SDICT_IMG_PNG;
2262             }
2263              
2264 0 0       0 if ( $file =~ /djv.?/i ) {
2265 0         0 return SDICT_IMG_DJVU;
2266             }
2267              
2268 0         0 return 0;
2269             }
2270              
2271              
2272             sub get_image_resolution ($) {
2273 0     0 0 0 my $file = $_[0];
2274              
2275 0 0       0 unless (open (IDENTITY, "identify $file |")) {
2276 0         0 warn "cannot run 'identify' from IM";
2277 0         0 return ();
2278             }
2279              
2280 0         0 my $str = q{};
2281              
2282 0         0 while () {
2283 0         0 chomp;
2284 0 0       0 if ( /$file/ ) {
2285 0         0 $str = $_;
2286 0         0 last;
2287             }
2288             }
2289 0         0 close IDENTITY;
2290              
2291 0         0 $str =~ s|$file\s+\w+\s+(\w+).*|$1|;
2292 0         0 return split (/x/, $str);
2293             }
2294              
2295              
2296             sub sort_words_list ($) {
2297 0     0 0 0 my $class = shift;
2298 0         0 prinfo 'Sorting word list';
2299 0         0 my @sorted = ();
2300              
2301 0         0 my @unsorted = @{ $class->{ words_list } };
  0         0  
2302 0         0 for (@unsorted) {
2303 0         0 $_ = decode ( "utf8", $_ );
2304             }
2305              
2306 0 0       0 if ( $class->{ sort } eq 'numeric') { # use numeric sorting
    0          
2307              
2308 0         0 prinfo "Using numeric sort method";
2309              
2310 0         0 @sorted = sort { $a<=>$b } ( @unsorted );
  0         0  
2311              
2312             }
2313             elsif ( $class->{ sort } ne 'Unicode::Collate') { # use table sorting
2314              
2315 0         0 $sort_table_pl = $class->{ sort };
2316 0 0       0 $sort_table_pl .= '.pl' if ( $sort_table_pl !~ /\.pl$/ );
2317              
2318 0         0 prinfo "Using sort table from library '$sort_table_pl'";
2319              
2320 0         0 eval ("require '$sort_table_pl'");
2321              
2322 0 0       0 if ( $@ ) {
2323 0         0 prerror "Unable to load .pl: '$@'";
2324 0         0 exit 1;
2325             }
2326              
2327 0         0 eval ("use Sort::ArbBiLex;");
2328              
2329 0 0       0 if ( $@ ) {
2330 0         0 prerror "Unable to load Sort::ArbBiLex: '$@'";
2331 0         0 exit 1;
2332             }
2333              
2334 0         0 *my_sort = Sort::ArbBiLex::maker ( $sort_table );
2335              
2336 0         0 @sorted = my_sort ( @unsorted );
2337             }
2338             else { # use Unicode::Collate sorting
2339              
2340 0         0 prinfo "Using Unicode::Collate for sorting";
2341              
2342 0         0 eval ("use Unicode::Collate;");
2343              
2344 0 0       0 if ( $@ ) {
2345 0         0 prerror "Unable to load Unicode::Collate: '$@'";
2346 0         0 exit 1;
2347             }
2348              
2349 0         0 my $collator = Unicode::Collate->new (
2350             upper_before_lower => 1
2351             );
2352              
2353 0 0       0 unless ( $collator ) {
2354 0         0 prerror 'Unable create sorting collator';
2355 0         0 exit 1;
2356             }
2357              
2358 0         0 @sorted = $collator->sort(@unsorted);
2359              
2360             }
2361              
2362              
2363 0 0       0 unless ( @sorted ) {
2364 0         0 prerror 'Unable sort';
2365 0         0 exit 1;
2366             }
2367              
2368 0         0 @unsorted = undef;
2369 0         0 for ( @sorted ) {
2370 0         0 $_ = encode ( "utf8", $_ );
2371             }
2372              
2373 0         0 $class->{ words_list } = undef;
2374 0         0 $class->{ words_list } = \@sorted;
2375 0         0 return 1;
2376             }
2377              
2378              
2379             sub sort_words_list_ ($) {
2380 0     0 0 0 my $class = shift;
2381 0         0 prinfo 'Sorting word list';
2382              
2383 0         0 my @unsorted = @{ $class->{ words_list } };
  0         0  
2384 0         0 for (@unsorted) {
2385 0         0 $_ = decode ( "utf8", $_ );
2386             }
2387              
2388 0         0 my $sorter = SortUTF8->new;
2389              
2390 0 0       0 unless ( $sorter->load_table ( 'latin-cyrillic.tbl' ) ) {
2391 0         0 prerror 'Unable create sorter';
2392 0         0 exit 1;
2393             }
2394              
2395 0         0 my @sorted = $sorter->sort ( @unsorted );
2396              
2397 0 0       0 unless ( @sorted ) {
2398 0         0 prerror 'Unable sort';
2399 0         0 exit 1;
2400             }
2401              
2402 0         0 @unsorted = undef;
2403 0         0 for ( @sorted ) {
2404 0         0 $_ = encode ( "utf8", $_ );
2405             }
2406              
2407 0         0 $class->{ words_list } = undef;
2408 0         0 $class->{ words_list } = \@sorted;
2409 0         0 return 1;
2410             }
2411              
2412              
2413             sub make_full_index ($) {
2414 0     0 0 0 my $class = shift;
2415 0         0 my $oufile = $class->{ outfile };
2416 0         0 my $temp_fi_file = $oufile . '-tmp2-' . $$;
2417 0         0 my $word;
2418             my $wl;
2419 0         0 my $i_prev = 0;
2420 0         0 my $i_next = 0;
2421 0         0 my $fpos = 0;
2422 0         0 my $wunit = q{};
2423              
2424 0         0 prinfo "Creating temporary file '$temp_fi_file'";
2425 0 0       0 unless ( sysopen ( FIF, $temp_fi_file, O_RDWR | O_CREAT ) ) {
2426 0         0 prerror "Unable create file '$temp_fi_file':$!";
2427 0         0 return 0;
2428             }
2429              
2430 0         0 $class->{ temp_fi_file } = $temp_fi_file;
2431              
2432              
2433 0         0 for $word ( @{ $class->{ words_list } } ) {
  0         0  
2434 0         0 $wl = length ( $word );
2435 0         0 $i_next = $wl + 4 + 2 + 2;
2436             $wunit = pack (
2437             "S2L",
2438             $i_next,
2439             $i_prev,
2440 0         0 $class->{ words_hash }->{ $word }
2441             )
2442             . $word;
2443              
2444 0         0 $fpos = sysseek( FIF, 0, 1 );
2445 0         0 syswrite ( FIF, $wunit );
2446 0         0 $i_prev = $i_next;
2447             }
2448              
2449             # lead out
2450 0         0 $wunit = pack ( "S2L", 0, $i_prev, 0 );
2451 0         0 syswrite ( FIF, $wunit );
2452              
2453 0         0 close FIF;
2454              
2455 0         0 $class->{ temp_fi_file } = $temp_fi_file;
2456 0         0 $class->{ temp_fi_file_size } = ( stat ( $temp_fi_file ) )[7];
2457              
2458 0         0 return 1;
2459             }
2460              
2461              
2462             sub make_short_index ($) {
2463 0     0 0 0 my $class = shift;
2464              
2465 0         0 my $oufile = $class->{ outfile };
2466 0         0 my $temp_si_file = $oufile . '-tmp3-' . $$;
2467              
2468 0         0 my $fpos = 0;
2469 0         0 my $last_s_index = q{};
2470 0         0 my %all_s_ndx = ();
2471 0         0 my $sindex_total = 0;
2472              
2473             my (
2474 0         0 $record,
2475             $cur_word_len,
2476             $cur_word_p,
2477             $cur_word,
2478             $cur_word_p_sub,
2479             $cur_word_sub,
2480             $extend,
2481             $unit,
2482             $i,
2483             %words_hash_short,
2484             @words_list_short,
2485             $j,
2486             %words_hash,
2487             @words_list
2488             );
2489              
2490 0         0 prinfo "Creating temporary file '$temp_si_file'";
2491              
2492 0 0       0 unless ( open ( SIF, "> $temp_si_file" ) ) {
2493 0         0 prerror "Cannot create $temp_si_file:$!";
2494 0         0 exit 1;
2495             }
2496              
2497 0         0 binmode SIF;
2498              
2499 0 0       0 unless ( sysopen( IF, $class->{ temp_fi_file }, O_RDONLY ) ) {
2500 0         0 prerror "Unable open file '", $class->{ temp_fi_file }, "':$!";
2501 0         0 exit 1;
2502             }
2503              
2504             #
2505             # reading all words from full index
2506             #
2507              
2508 0         0 %words_hash = ();
2509 0         0 @words_list = ();
2510              
2511 0         0 while (1) {
2512 0         0 $fpos = sysseek( IF, 0, 1 );
2513              
2514 0 0       0 unless ( sysread ( IF, $record, 8, 0 ) ) {
2515 0         0 prinfo "Looks like EOF";
2516 0         0 last;
2517             }
2518            
2519 0         0 $cur_word_len = ( unpack (
2520             "S",
2521             substr ( $record, 0, 2 )
2522             )
2523             )[0];
2524              
2525 0 0       0 unless ($cur_word_len) {
2526 0         0 prinfo "Last record, quit";
2527 0         0 last;
2528             }
2529            
2530             sysread (
2531 0         0 IF,
2532             $cur_word,
2533             $cur_word_len - 8
2534             );
2535              
2536 0         0 $cur_word_p = decode ( "utf8", $cur_word );
2537              
2538 0         0 push ( @words_list, $cur_word_p );
2539 0         0 $words_hash{$cur_word_p} = $fpos;
2540             # print ">>$cur_word_p<< >>$fpos<< \n";
2541             }
2542              
2543             #
2544             # Making indices
2545             #
2546 0         0 %words_hash_short = ();
2547 0         0 @words_list_short = ();
2548              
2549 0         0 my $slev_total = $class->{ slevels };
2550              
2551 0         0 prinfo "Short index levels: $slev_total";
2552              
2553 0         0 for ( $i = 1; $i <= $slev_total; $i++ ) {
2554              
2555 0         0 prinfo "Making with length $i";
2556              
2557 0         0 for $j ( @words_list ) {
2558              
2559 0         0 $cur_word_p_sub = substr ( $j, 0, $i );
2560              
2561 0 0       0 if ( exists ( $words_hash_short{ $cur_word_p_sub } ) ) {
2562 0         0 $words_hash_short{ $cur_word_p_sub }++;
2563             #prinfo "index '$cur_word_p_sub' already exists, skip";
2564 0         0 next;
2565             }
2566              
2567 0         0 $words_hash_short{ $cur_word_p_sub }++;
2568 0         0 $fpos = $words_hash{ $j };
2569              
2570 0         0 $cur_word_sub = encode( "utf8", $cur_word_p_sub );
2571             # $cur_word_sub = $cur_word_p_sub;
2572              
2573 0         0 push ( @words_list_short, $cur_word_sub );
2574              
2575             # $cur_word_sub = $cur_word_p_sub;
2576 0         0 from_to ( $cur_word_sub, "utf8", "UTF-32LE" );
2577              
2578 0         0 $extend = q{};
2579              
2580 0 0       0 if ( length ( $cur_word_p_sub ) < $slev_total ) {
2581 0         0 for (
2582             my $i=0;
2583             $i < ($slev_total - length($cur_word_p_sub));
2584             $i++ ) {
2585 0         0 $_ = pack( "L", 0 );
2586 0         0 $extend .= $_;
2587             }
2588             }
2589              
2590 0         0 $unit = $cur_word_sub . $extend . pack ( "L", $fpos );
2591             #$_ = length ($unit); print "L>$_<\n";
2592              
2593 0         0 print SIF $unit;
2594 0         0 $sindex_total++;
2595             }
2596             }
2597              
2598 0         0 close SIF;
2599 0         0 close IF;
2600              
2601            
2602 0         0 $class->{ temp_si_file_size_unc } = ( stat ( $temp_si_file ) )[7];
2603 0         0 $class->compress_s_index( $temp_si_file );
2604              
2605 0         0 $class->{ sindex_total } = $sindex_total;
2606 0         0 $class->{ temp_si_file } = $temp_si_file;
2607 0         0 $class->{ temp_si_file_size } = ( stat ( $temp_si_file ) )[7];
2608              
2609 0         0 my $ucs = $class->{ temp_si_file_size_unc };
2610 0         0 my $ccs = $class->{ temp_si_file_size };
2611              
2612 0         0 prinfo "Short index info: $ucs / $ccs";
2613              
2614 0 0       0 if ( $ucs > SDICT_SINDEX_WARN ) {
2615             #prinfo 'WARN! sindex too big';
2616             }
2617              
2618 0         0 return 1;
2619             }
2620              
2621              
2622             sub join_files ($) {
2623 0     0 0 0 my $class = shift;
2624 0         0 my $ofile = $class->{ outfile };
2625 0         0 my $file;
2626              
2627 0         0 $file = $class->{ temp_si_file };
2628 0         0 prinfo "Merging '$file' into '$ofile'";
2629 0         0 Sdict::Utils::merge ($file, $ofile);
2630              
2631 0         0 $file = $class->{ temp_fi_file };
2632 0         0 prinfo "Merging '$file' into '$ofile'";
2633 0         0 Sdict::Utils::merge ($file, $ofile);
2634              
2635 0         0 $file = $class->{ temp_afile };
2636 0         0 prinfo "Merging '$file' into '$ofile'";
2637 0         0 Sdict::Utils::merge ($file, $ofile);
2638              
2639 0 0 0     0 if ( $class->{ parse_embedded } && $class->{ embedded_total } )
2640             {
2641 0         0 $file = $class->{ temp_bin1_ndx_file };
2642 0         0 prinfo "Merging '$file' into '$ofile'";
2643 0         0 Sdict::Utils::merge ($file, $ofile);
2644              
2645 0         0 $file = $class->{ temp_bin1_file };
2646 0         0 prinfo "Merging '$file' into '$ofile'";
2647 0         0 Sdict::Utils::merge ($file, $ofile);
2648             }
2649              
2650 0         0 return 1;
2651             }
2652              
2653              
2654             sub cleanups ($) {
2655 0     0 0 0 my $class = shift;
2656              
2657 0         0 prinfo "Removing '", $class->{ temp_afile }, "'";
2658 0         0 unlink ( $class->{ temp_afile } );
2659              
2660 0         0 prinfo "Removing '", $class->{ temp_fi_file }, "'";
2661 0         0 unlink ( $class->{ temp_fi_file } );
2662              
2663 0         0 prinfo "Removing '", $class->{ temp_si_file }, "'";
2664 0         0 unlink ( $class->{ temp_si_file } );
2665              
2666 0 0       0 if ( $class->{ parse_embedded } )
2667             {
2668 0         0 prinfo "Removing '", $class->{ temp_bin1_file }, "'";
2669 0         0 unlink ( $class->{ temp_bin1_file } );
2670 0         0 prinfo "Removing '", $class->{ temp_bin1_ndx_file }, "'";
2671 0         0 unlink ( $class->{ temp_bin1_ndx_file } );
2672              
2673             }
2674              
2675 0         0 return 1;
2676             }
2677              
2678              
2679             sub create_unit ($$) {
2680 0     0 0 0 my ( $class, $text ) = @_[0,1];
2681              
2682 0         0 my $unit = q{};
2683 0         0 my $ctext = q{};
2684              
2685              
2686 0 0       0 if ( $class->{ compressor } eq COMPRESSOR_NONE ) {
    0          
    0          
2687 0         0 $unit = pack ( "L", length( $text ) );
2688 0         0 $unit .= $text;
2689 0         0 return $unit;
2690              
2691             }
2692             elsif ( $class->{ compressor } eq 'gzip' ) {
2693 0         0 $ctext = compress ( $text, GZIP_COMPRESSION_LEVEL );
2694 0         0 $unit = pack ( "L", length ( $ctext ) );
2695              
2696 0 0       0 unless ( $ctext ) {
2697 0         0 prerror ("Compression failed for '$text'");
2698 0         0 exit 1;
2699             }
2700              
2701 0         0 $unit .= $ctext;
2702 0         0 return $unit;
2703              
2704             }
2705             elsif ( $class->{ compressor } eq 'bzip2' ) {
2706              
2707 0         0 $ctext = Compress::Bzip2::compress ( $text, BZIP2_COMPRESSION_LEVEL );
2708 0         0 $unit = pack ( "L", length($ctext ) );
2709              
2710 0 0       0 unless ( $ctext ) {
2711 0         0 prerror ("Compression failed for '$text'");
2712 0         0 exit 1;
2713             }
2714              
2715 0         0 $unit .= $ctext;
2716 0         0 return $unit;
2717             }
2718              
2719              
2720 0         0 prerror 'Unsupported compression method';
2721 0         0 exit 1;
2722             }
2723              
2724              
2725             sub decompress_unit ($$) {
2726 4     4 0 10 my ( $class, $unit ) = @_[0,1];
2727 4         8 my $text = q{};
2728 4         6 my $ctext = q{};
2729              
2730 4 50       22 if ( $class->{ compressor } eq COMPRESSOR_NONE ) {
    50          
    0          
2731 0         0 $text = substr ( $unit, 4 );
2732 0         0 return $text;
2733              
2734             }
2735             elsif ( $class->{ compressor } eq COMPRESSOR_GZIP ) {
2736 4         7 $ctext = substr ( $unit, 4 );
2737 4         13 $text = uncompress ( $ctext );
2738 4         255 return $text;
2739              
2740             }
2741             elsif ( $class->{ compressor } eq COMPRESSOR_BZIP2 ) {
2742 0         0 $ctext = substr ( $unit, 4 );
2743 0         0 $text = Compress::Bzip2::uncompress ( $ctext );
2744 0         0 return $text;
2745             }
2746              
2747 0         0 prerror 'Wrong compression type';
2748 0         0 exit 1;
2749              
2750             }
2751              
2752              
2753             sub compress_s_index ($$) {
2754 0     0 0 0 my ( $class, $file ) = @_[0,1];
2755 0         0 local $/ = undef;
2756 0         0 my $content = q{};
2757 0         0 my $content_c = q{};
2758              
2759 0         0 prinfo "Compressing file '$file'";
2760              
2761 0 0       0 if ( $class->{ compressor } eq COMPRESSOR_NONE ) {
    0          
    0          
2762 0         0 prinfo "No compressing needed'";
2763 0         0 return 1;
2764             }
2765             elsif ( $class->{ compressor } eq COMPRESSOR_GZIP ) {
2766 0 0       0 unless ( open F, "< $file" ) {
2767 0         0 prerror "Unable open file '$file':$!";
2768 0         0 exit 1;
2769             }
2770 0         0 binmode F;
2771              
2772 0         0 $content = ;
2773 0         0 close F;
2774              
2775 0 0       0 unless ( length ( $content ) ) {
2776 0         0 prerror "Zero file length";
2777 0         0 exit 1;
2778             }
2779              
2780 0         0 prinfo "Short index uncompressed", length ( $content ), "byte(s)";
2781              
2782 0         0 $content_c = compress ( $content, GZIP_COMPRESSION_LEVEL );
2783              
2784 0 0       0 unless ( length( $content_c ) ) {
2785 0         0 prerror "Compression failed";
2786 0         0 exit 1;
2787             }
2788              
2789 0         0 prinfo "Short index compressed", length ( $content_c ), "byte(s)";
2790              
2791 0 0       0 unless ( open F, "> $file" ) {
2792 0         0 prerror "Unable open file for writing '$file':$!";
2793 0         0 exit 1;
2794             }
2795 0         0 binmode F;
2796              
2797 0         0 print F $content_c;
2798 0         0 close F;
2799              
2800 0         0 return 1;
2801              
2802             }
2803             elsif ( $class->{ compressor } eq COMPRESSOR_BZIP2 ) {
2804 0 0       0 unless ( open F, "< $file" ) {
2805 0         0 prerror "Unable open file '$file':$!";
2806 0         0 exit 1;
2807             }
2808              
2809 0         0 $content = ;
2810 0         0 close F;
2811            
2812 0 0       0 unless ( length($content ) ) {
2813 0         0 prerror "Zero file length";
2814 0         0 exit 1;
2815             }
2816              
2817 0         0 prinfo "Short index uncompressed", length ( $content ), "byte(s)";
2818              
2819 0         0 $content_c = Compress::Bzip2::compress ( $content, BZIP2_COMPRESSION_LEVEL );
2820              
2821 0 0       0 unless ( length($content_c ) ) {
2822 0         0 prerror "Compression failed";
2823 0         0 exit 1;
2824             }
2825              
2826 0         0 prinfo "Short index compressed", length ( $content_c ), "byte(s)";
2827              
2828 0 0       0 unless ( open F, "> $file" ) {
2829 0         0 prerror "Unable open file for writing '$file':$!";
2830 0         0 exit 1;
2831             }
2832              
2833 0         0 print F $content_c;
2834 0         0 close F;
2835              
2836 0         0 return 1;
2837             }
2838              
2839 0         0 return 0;
2840             }
2841              
2842              
2843             sub get_embedded_image ($) {
2844 0     0 0 0 my $class = shift;
2845 0         0 my $imgno = shift;
2846 0         0 my $img = {};
2847 0         0 my $tmp = 0;
2848              
2849 0 0       0 unless ( $class->{ header }->{ dct_v2 } ) {
2850 0         0 prerror 'No embedded objects found';
2851 0         0 return $img;
2852             }
2853              
2854 0 0 0     0 if ( ! defined ($imgno) || ($imgno +1 ) > $class->{ header }->{ embedded_total } ) {
2855 0         0 prerror "No such object, num $imgno";
2856 0         0 return $img;
2857             }
2858              
2859              
2860 0         0 my $file = $class->{ infile_handler };
2861              
2862 0 0       0 unless ( sysseek ( $file, $class->{ header }->{ embedded_offset } + 4 * ( $imgno + 1 ), 0 ) )
2863             {
2864 0         0 prerror "Seek error: $!";
2865 0         0 return $img;
2866             }
2867              
2868 0 0       0 unless (sysread ($file, $tmp, 4, 0)) {
2869 0         0 prerror "Sysread error: $!";
2870 0         0 return $img;
2871             }
2872              
2873 0         0 $tmp = unpack ( "L", $tmp );
2874 0         0 prinfo 'image ofset= ', sprintf ( "0x%x", $tmp ) ;
2875              
2876 0         0 my $ifoff = $class->{ header }->{ embedded_offset } + $tmp ;
2877 0         0 prinfo 'unit ofset= ', sprintf ( "0x%x", $ifoff ) ;
2878              
2879              
2880 0 0       0 unless ( sysseek ( $file, $ifoff, 0 ) )
2881             {
2882 0         0 prerror "Seek error: $!";
2883 0         0 return $img;
2884             }
2885              
2886 0 0       0 unless (sysread ($file, $tmp, 4, 0)) {
2887 0         0 prerror "Sysread error: $!";
2888 0         0 return $img;
2889             }
2890              
2891 0         0 my $ul = unpack ( "L", $tmp );
2892 0         0 prinfo 'unit length= ', sprintf ( "0x%x", $ul ) ;
2893              
2894 0 0       0 unless (sysread ($file, $tmp, 5, 0)) {
2895 0         0 prerror "Sysread error: $!";
2896 0         0 return $img;
2897             }
2898              
2899              
2900 0         0 my $img_type = unpack ( "C", substr ( $tmp, 0, 1 ) );
2901 0         0 my $img_width = unpack ( "S", substr ( $tmp, 1, 2 ) );
2902 0         0 my $img_height = unpack ( "S", substr ( $tmp, 3, 2 ) );
2903 0         0 my $img_len = $ul - 5; # 1 - 2 - 2 ;
2904 0         0 prinfo "image type= $img_type, size= $img_width x $img_height, len= $img_len";
2905              
2906 0         0 my $img_raw = q{};
2907              
2908 0 0       0 unless (sysread ($file, ${ $img->{ raw } } , $img_len, 0)) {
  0         0  
2909 0         0 prerror "Sysread error: $!";
2910 0         0 return $img;
2911             }
2912              
2913 0         0 $img -> { type } = $img_type ;
2914 0         0 $img -> { width } = $img_width ;
2915 0         0 $img -> { height } = $img_height ;
2916 0         0 $img -> { len } = $img_len ;
2917              
2918              
2919 0 0 0     0 if ( $img -> { type } == SDICT_IMG_PNG ||
      0        
2920             $img -> { type } == SDICT_IMG_GIF ||
2921             $img -> { type } == SDICT_IMG_JPEG ) {
2922 0         0 return $img;
2923             }
2924              
2925              
2926 0 0 0     0 if ( $img -> { type } != SDICT_IMG_JB2 &&
2927             $img -> { type } != SDICT_IMG_IW44 ) {
2928 0         0 return {};
2929             }
2930              
2931              
2932 0 0 0     0 if ( $img -> { type } != SDICT_IMG_JB2 &&
2933             $img -> { type } != SDICT_IMG_IW44 ) {
2934 0         0 return {};
2935             }
2936              
2937 0         0 my $chunk = q{};
2938              
2939 0 0       0 if ( $img -> { type } == SDICT_IMG_JB2 ) {
    0          
2940 0         0 prinfo 'convert JB2';
2941 0         0 $chunk = 'Sjbz';
2942             }
2943             elsif ( $img -> { type } == SDICT_IMG_IW44 ) {
2944 0         0 prinfo 'convert IW44';
2945 0         0 $chunk = 'BG44';
2946             }
2947              
2948 0         0 my $file_tmp1 = $ENV{'HOME'} . "/.ptksdict-$$-tmp1.djvu";
2949 0         0 my $file_tmp2 = $ENV{'HOME'} . "/.ptksdict-$$-tmp2.png";
2950 0 0       0 unless ( open T1, "> $file_tmp1" )
2951             {
2952 0         0 prerror "cannot create $file_tmp1: $!";
2953 0         0 return {};
2954             }
2955              
2956 0         0 print T1 'AT&TFORM', pack ( "N", $img_len + 8 + 4 + 8 + 10 ); ;
2957 0         0 print T1 'DJVUINFO', pack ( "N", 10 ) ;
2958 0         0 print T1 pack ( "n2C6", $img_width, $img_height, 0x18, 0x0, 0x2c, 0x1, 0x16, 0x1 ) ;
2959 0         0 print T1 $chunk , pack ( "N", $img_len );
2960 0         0 print T1 ${ $img->{ raw } } ;
  0         0  
2961 0         0 close T1;
2962              
2963 0         0 system ("ddjvu -format=ppm $file_tmp1 | convert -verbose - $file_tmp2");
2964              
2965 0         0 unlink ( $file_tmp1 );
2966              
2967 0 0       0 unless ( open ( T2, "< $file_tmp2" ) ) {
2968 0         0 prerror "cannot open $file_tmp2: $!";
2969 0         0 return {};
2970             }
2971              
2972             {
2973 0         0 local $/ = undef;
  0         0  
2974 0         0 ${ $img->{ raw } } = ;
  0         0  
2975             }
2976 0         0 close T2;
2977              
2978 0         0 unlink ($file_tmp2);
2979              
2980 0         0 return $img;
2981             }
2982              
2983              
2984             sub get_embedded_sound ($) {
2985 0     0 0 0 my $class = shift;
2986 0         0 my $sndno = shift;
2987 0         0 my $snd = {};
2988 0         0 my $tmp = 0;
2989              
2990 0 0       0 unless ( $class->{ header }->{ dct_v2 } ) {
2991 0         0 prerror 'No embedded objects found';
2992 0         0 return $snd;
2993             }
2994              
2995 0 0 0     0 if ( ! defined ($sndno) || ($sndno +1 ) > $class->{ header }->{ embedded_total } ) {
2996 0         0 prerror "No such object, num $sndno";
2997 0         0 return $snd;
2998             }
2999              
3000              
3001 0         0 my $file = $class->{ infile_handler };
3002              
3003 0 0       0 unless ( sysseek ( $file, $class->{ header }->{ embedded_offset } + 4 * ( $sndno + 1 ), 0 ) )
3004             {
3005 0         0 prerror "Seek error: $!";
3006 0         0 return $snd;
3007             }
3008              
3009 0 0       0 unless (sysread ($file, $tmp, 4, 0)) {
3010 0         0 prerror "Sysread error: $!";
3011 0         0 return $snd;
3012             }
3013              
3014 0         0 $tmp = unpack ( "L", $tmp );
3015 0         0 prinfo 'sound ofset= ', sprintf ( "0x%x", $tmp ) ;
3016              
3017 0         0 my $ifoff = $class->{ header }->{ embedded_offset } + $tmp ;
3018 0         0 prinfo 'unit ofset= ', sprintf ( "0x%x", $ifoff ) ;
3019              
3020              
3021 0 0       0 unless ( sysseek ( $file, $ifoff, 0 ) )
3022             {
3023 0         0 prerror "Seek error: $!";
3024 0         0 return $snd;
3025             }
3026              
3027 0 0       0 unless (sysread ($file, $tmp, 4, 0)) {
3028 0         0 prerror "Sysread error: $!";
3029 0         0 return $snd;
3030             }
3031              
3032 0         0 my $ul = unpack ( "L", $tmp );
3033 0         0 prinfo 'unit length= ', sprintf ( "0x%x", $ul ) ;
3034              
3035 0 0       0 unless (sysread ($file, $tmp, 3, 0)) {
3036 0         0 prerror "Sysread error: $!";
3037 0         0 return $snd;
3038             }
3039              
3040 0         0 my $snd_type = unpack ( "C", substr ( $tmp, 0, 1 ) );
3041 0         0 my $snd_len = unpack ( "S", substr ( $tmp, 1, 2 ) );
3042 0         0 my $snd_file_len = $ul - 3; # 1 - 2 ;
3043              
3044 0         0 prinfo "snd type= $snd_type, len= $snd_len (x0.1sec)";
3045              
3046 0         0 my $snd_raw = q{};
3047              
3048 0 0       0 unless (sysread ($file, ${ $snd->{ raw } } , $snd_file_len, 0)) {
  0         0  
3049 0         0 prerror "Sysread error: $!";
3050 0         0 return $snd;
3051             }
3052              
3053 0         0 $snd -> { type } = $snd_type ;
3054 0         0 $snd -> { len } = $snd_len ;
3055 0         0 $snd -> { file_len } = $snd_len ;
3056              
3057 0         0 return $snd;
3058             }
3059              
3060              
3061             #
3062             # Sdict::Utils;
3063             #
3064             package Sdict::Utils;
3065              
3066 2     2   30 use strict;
  2         4  
  2         97  
3067 2     2   14 use IO::File;
  2         5  
  2         364  
3068              
3069              
3070             use constant {
3071              
3072 2         3296 BUFFER_SIZE => 10240 ,
3073 2     2   14 };
  2         5  
3074              
3075             sub merge {
3076 0     0   0 my ($file, $ofile) = @_;
3077              
3078 0 0       0 unless (open (IF, "< $file")) {
3079 0         0 Sdict::prerror "can't open file $file: $!";
3080 0         0 exit 1;
3081             }
3082              
3083 0 0       0 unless (open (OF, ">> $ofile")) {
3084 0         0 Sdict::prerror "can't open file $ofile: $!";
3085 0         0 close (IF);
3086 0         0 exit 1;
3087             }
3088              
3089 0         0 binmode (IF);
3090 0         0 binmode (OF);
3091              
3092 0         0 my $buf = q{};
3093 0         0 my $rlen = 0;
3094              
3095 0         0 while ( ($rlen = read (IF, $buf, BUFFER_SIZE)) ) {
3096 0         0 print OF $buf;
3097 0         0 $buf = q{};
3098             }
3099              
3100 0         0 close (IF);
3101 0         0 close (OF);
3102             }
3103              
3104             sub parse_djvu_file {
3105 0     0   0 my ($file) = @_;
3106 0         0 my $djvu = {};
3107 0         0 my ($buf, $buf2, $chunk, $chunk_len, $chunk_raw);
3108              
3109 0         0 Sdict::prinfo "Parsing file '$file'";
3110              
3111 0 0       0 unless ( sysopen ( DJV, $file, O_RDONLY ) ) {
3112 0         0 Sdict::prerror "Unable to open file '$file':$!";
3113 0         0 return $djvu;
3114             }
3115 0         0 binmode DJV;
3116              
3117 0 0       0 unless ( sysread ( DJV, $buf, 4, 0 ) ) {
3118 0         0 Sdict::prerror "Unable to sysread from file '$file':$!";
3119 0         0 close DJV;
3120 0         0 return $djvu;
3121             }
3122              
3123 0 0       0 if ( $buf eq 'AT&T' ) {
3124 0 0       0 unless ( sysread ( DJV, $buf, 4, 0 ) ) {
3125 0         0 Sdict::prerror "Unable to sysread from file '$file':$!";
3126 0         0 close DJV;
3127 0         0 return $djvu;
3128             }
3129             }
3130              
3131 0 0       0 if ( $buf ne 'FORM' ) {
3132 0         0 Sdict::prerror 'Wrong signature';
3133 0         0 close DJV;
3134 0         0 return $djvu;
3135             }
3136              
3137 0 0       0 unless ( sysread ( DJV, $buf, 4, 0 ) ) {
3138 0         0 Sdict::prerror "Unable to sysread from file '$file':$!";
3139 0         0 close DJV;
3140 0         0 return $djvu;
3141             }
3142              
3143 0         0 my $len = unpack ("N", $buf) + sysseek ( DJV, 0, SEEK_CUR );
3144            
3145 0 0       0 unless ( sysread ( DJV, $buf, 8, 0 ) ) {
3146 0         0 Sdict::prerror "Unable to sysread from file '$file':$!";
3147 0         0 close DJV;
3148 0         0 return $djvu;
3149             }
3150              
3151 0 0       0 if ( $buf ne 'DJVUINFO' ) {
3152 0         0 Sdict::prerror 'Wrong signature';
3153 0         0 close DJV;
3154 0         0 return $djvu;
3155             }
3156              
3157              
3158 0 0       0 unless ( sysread ( DJV, $buf, 4, 0 ) ) {
3159 0         0 Sdict::prerror "Unable to sysread from file '$file':$!";
3160 0         0 close DJV;
3161 0         0 return $djvu;
3162             }
3163 0         0 my $next_seek = unpack ("N", $buf) + sysseek ( DJV, 0, SEEK_CUR );
3164              
3165              
3166 0 0       0 unless ( sysread ( DJV, $buf, 10, 0 ) ) {
3167 0         0 Sdict::prerror "Unable to sysread from file '$file':$!";
3168 0         0 close DJV;
3169 0         0 return $djvu;
3170             }
3171 0         0 my $w = unpack ("n", substr ($buf, 0, 2) );
3172 0         0 my $h = unpack ("n", substr ($buf, 2, 2) );
3173 0 0 0     0 if (!$w || !$h) {
3174 0         0 Sdict::prerror "Unable to get image size";
3175 0         0 close DJV;
3176 0         0 return $djvu;
3177             }
3178 0         0 $djvu->{ width } = $w;
3179 0         0 $djvu->{ height } = $h;
3180              
3181 0         0 sysseek ( DJV, $next_seek, 0 );
3182              
3183 0         0 my @bad_chunks = qw / Djbz INCL Fgbz /;
3184              
3185 0         0 while ( sysseek ( DJV, 0, SEEK_CUR ) < $len )
3186             {
3187 0 0       0 unless ( sysread ( DJV, $chunk, 4, 0 )==4 ) {
3188 0         0 Sdict::prerror "Unable to sysread from file '$file':$!";
3189 0         0 close DJV;
3190 0         0 return $djvu;
3191             }
3192              
3193 0 0       0 unless ( sysread ( DJV, $buf2, 4, 0 ) ) {
3194 0         0 Sdict::prerror "Unable to sysread from file '$file':$!";
3195 0         0 close DJV;
3196 0         0 return $djvu;
3197             }
3198              
3199 0         0 $chunk_len = unpack ("N", $buf2);
3200              
3201 0 0       0 unless ( sysread ( DJV, $chunk_raw, $chunk_len, 0 ) ) {
3202 0         0 Sdict::prerror "Unable to sysread from file '$file':$!";
3203 0         0 close DJV;
3204 0         0 return $djvu;
3205             }
3206              
3207 0         0 Sdict::prinfo "chunk= $chunk, chunk_len= " , sprintf ( "0x%x", $chunk_len ), ' raw size= ', sprintf ( "0x%x", length ($chunk_raw) );
3208              
3209 0 0       0 if ( grep (/$chunk/, @bad_chunks) ) {
3210 0         0 Sdict::prerror "Illegal chunk '$chunk' in file";
3211 0         0 close DJV;
3212 0         0 return $djvu;
3213             }
3214              
3215 0 0       0 if ( $chunk eq 'Sjbz' ) {
3216 0         0 $djvu->{ sjbz } = $chunk_raw;
3217 0         0 last;
3218             }
3219              
3220 0 0       0 if ( $chunk eq 'BG44' ) {
3221 0         0 push @{ $djvu->{ bg44 } }, $chunk_raw ;
  0         0  
3222             }
3223              
3224 0 0       0 if (sysseek ( DJV, 0, SEEK_CUR ) & 1) {
3225 0         0 sysseek ( DJV, 1, SEEK_CUR );
3226             }
3227             }
3228 0         0 close DJV;
3229              
3230              
3231 0 0 0     0 if ( defined ( @{ $djvu->{ bg44 } } ) && @{ $djvu->{ bg44 } } )
  0         0  
  0         0  
3232             {
3233 0         0 my $bg44 = shift ( @{ $djvu->{ bg44 } } );
  0         0  
3234              
3235 0         0 my $serial = unpack ("C", substr ( $bg44, 0, 1) );
3236 0         0 my $slices = unpack ("C", substr ( $bg44, 1, 1) );
3237              
3238 0         0 Sdict::prinfo "first part (serial $serial), $slices slices";
3239 0 0       0 return {} unless $slices;
3240            
3241 0         0 my $full_bg44 = $bg44;
3242            
3243             # TODO
3244 0         0 if (0 && scalar ( @{ $djvu->{ bg44 } } ) ) {
3245             for $bg44 ( @{ $djvu->{ bg44 } } )
3246             {
3247             $serial = unpack ("C", substr ( $bg44, 0, 1) );
3248             my $slices_here = unpack ("C", substr ( $bg44, 1, 1) );
3249             Sdict::prinfo "next part (serial $serial), $slices_here slices";
3250             return {} unless $slices_here;
3251             $slices += $slices_here;
3252             $full_bg44 .= substr ($bg44, 2);
3253             }
3254             }
3255              
3256 0         0 Sdict::prinfo "slices in total $slices";
3257 0 0       0 return {} if ($slices > 255);
3258              
3259 0         0 substr $full_bg44, 1, 1, pack ("C", $slices );
3260 0         0 $djvu->{ bg44 } = undef;
3261 0         0 $djvu->{ bg44 } = $full_bg44;
3262             }
3263              
3264 0         0 return $djvu;
3265             }
3266              
3267              
3268             1;
3269              
3270              
3271             __END__