1#+############################################################################## 2# 3# latex2html.pm: interface to LaTeX2HTML 4# 5# Copyright (C) 1999, 2000, 2003, 2005, 2006, 2009, 2011, 2013 6# Free Software Foundation, Inc. 7# 8# This program is free software; you can redistribute it and/or modify 9# it under the terms of the GNU General Public License as published by 10# the Free Software Foundation; either version 3 of the License, 11# or (at your option) any later version. 12# 13# This program is distributed in the hope that it will be useful, 14# but WITHOUT ANY WARRANTY; without even the implied warranty of 15# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16# GNU General Public License for more details. 17# 18# You should have received a copy of the GNU General Public License 19# along with this program. If not, see <http://www.gnu.org/licenses/>. 20# 21# This code was taken from the main texi2html file in 2006. 22# Certainly originally written by Olaf Bachmann. 23# Adapted from texi2html T2h_l2h.pm in 2011. 24# 25#-############################################################################## 26 27require 5.0; 28use strict; 29 30use Cwd; 31use File::Copy; 32use File::Spec; 33 34# For __( 35use Texinfo::Common; 36 37texinfo_register_handler('structure', \&l2h_process); 38texinfo_register_handler('finish', \&l2h_finish); 39 40texinfo_register_command_formatting('math', \&l2h_do_tex); 41texinfo_register_command_formatting('tex', \&l2h_do_tex); 42texinfo_register_command_formatting('displaymath', \&l2h_do_tex); 43 44# name/location of latex2html program 45set_from_init_file('L2H_L2H', 'latex2html'); 46 47# If this is set the actual call to latex2html is skipped. The previously 48# generated content is reused, instead. 49# If set to 0, the cache is not used. 50# If undef the cache is used for as many tex fragments as possible 51# and for the remaining the command is run. 52set_from_init_file('L2H_SKIP', undef); 53 54# If this is set l2h uses the specified directory for temporary files. The path 55# leading to this directory may not contain a dot (i.e., a "."); 56# otherwise, l2h will fail. 57set_from_init_file('L2H_TMP', ''); 58 59# If set, l2h uses the file as latex2html init file 60set_from_init_file('L2H_FILE', undef); 61 62# if this is set the intermediate files generated by texi2html in relation with 63# latex2html are cleaned (they all have the prefix <document name>_l2h_). 64set_from_init_file('L2H_CLEAN', 1); 65 66 67# latex2html conversions consist of 2 stages: 68# 1) l2h_process 69# to latex: Put "latex" code into a latex file 70# (l2h_to_latex, l2h_finish_to_latex) 71# to html: Use latex2html to generate corresponding html code and images 72# (l2h_to_html) 73# from html: Extract generated code and images from latex2html run 74# (l2h_init_from_html) 75# 2) l2h_do_tex called each time a @tex or @math command is encountered 76# in the output tree. 77 78# init l2h defaults for files and names 79 80my ($l2h_name, $l2h_latex_file, $l2h_cache_file, $l2h_html_file, $l2h_prefix); 81 82# holds the status of latex2html operations. If 0 it means that there was 83# an error 84my $status = 0; 85 86my $debug; 87my $verbose; 88my $docu_rdir; 89my $docu_volume; 90my $docu_directories; 91my $docu_name; 92 93my %commands_counters; 94 95# init_from_html 96my $extract_error_count; 97my $invalid_counter_count; 98 99# change_image_file_names 100my %l2h_img; # associate src file to destination file 101 # such that files are not copied twice 102my $image_count; 103 104# do_tex 105my $html_output_count = 0; # html text outputed in html result file 106 107########################## 108# 109# First stage: Generation of Latex file 110# Initialize with: init 111# Add content with: l2h_to_latex ($text) --> HTML placeholder comment 112# Finish with: finish_to_latex 113# 114 115my $l2h_latex_preamble = <<EOT; 116% This document was automatically generated by the l2h extenstion of texi2html 117% DO NOT EDIT !!! 118\\documentclass{article} 119\\usepackage{html} 120\\begin{document} 121EOT 122 123my $l2h_latex_closing = <<EOT; 124\\end{document} 125EOT 126 127my %l2h_to_latex = (); # associate a latex text with the index in the 128 # html result array. 129my @l2h_to_latex = (); # array used to associate the index with 130 # the original latex text. 131my $latex_count = 0; # number of latex texts really stored 132my $latex_converted_count = 0; # number of latex texts passed through latex2html 133my $to_latex_count = 0; # total number of latex texts processed 134my $cached_count = 0; # number of cached latex texts 135my %l2h_cache = (); # the cache hash. Associate latex text with 136 # html from the previous run 137my @l2h_from_html; # array of resulting html 138 139my %global_count = (); # associate a command name and the 140 # corresponding counter to the index in the 141 # html result array 142 143# set $status to 1, if l2h could be initalized properly, to 0 otherwise 144sub l2h_process($$) 145{ 146 my $self = shift; 147 my $document_root = shift; 148 %l2h_to_latex = (); # associate a latex text with the index in the 149 # html result array. 150 @l2h_to_latex = (); # array used to associate the index with 151 # the original latex text. 152 $latex_count = 0; # number of latex texts really stored 153 $latex_converted_count = 0; # number of latex texts passed through latex2html 154 $to_latex_count = 0; # total number of latex texts processed 155 $cached_count = 0; # number of cached latex texts 156 %l2h_cache = (); # the cache hash. Associate latex text with 157 # html from the previous run 158 @l2h_from_html = (); # array of resulting html 159 160 %global_count = (); # associate a command name and the 161 # corresponding counter to the index in the 162 # html result array 163 %commands_counters = (); 164 $extract_error_count = 0; 165 $invalid_counter_count = 0; 166 %l2h_img = (); # associate src file to destination file 167 # such that files are not copied twice 168 $image_count = 1; 169 170 $html_output_count = 0; # html text outputed in html result file 171 $status = 0; 172 return if (defined($self->get_conf('OUTFILE')) 173 and $Texinfo::Common::null_device_file{$self->get_conf('OUTFILE')}); 174 175 176 $docu_name = $self->{'document_name'}; 177 $docu_rdir = $self->{'destination_directory'}; 178 $docu_rdir = '' if (!defined($docu_rdir)); 179 my $no_file; 180 ($docu_volume, $docu_directories, $no_file) 181 = File::Spec->splitpath($docu_rdir, 1); 182 $l2h_name = "${docu_name}_l2h"; 183 $l2h_latex_file = File::Spec->catpath($docu_volume, $docu_directories, 184 "${l2h_name}.tex"); 185 $l2h_cache_file = File::Spec->catpath($docu_volume, $docu_directories, 186 "${docu_name}-l2h_cache.pm"); 187 # destination dir -- generated images are put there, should be the same 188 # as dir of enclosing html document -- 189 $l2h_html_file = File::Spec->catpath($docu_volume, $docu_directories, 190 "${l2h_name}.html"); 191 $l2h_prefix = "${l2h_name}_"; 192 $debug = $self->get_conf('DEBUG'); 193 $verbose = $self->get_conf('VERBOSE'); 194 195 unless ($self->get_conf('L2H_SKIP')) { 196 unless (open(L2H_LATEX, ">$l2h_latex_file")) { 197 $self->document_error(sprintf(__( 198 "l2h: could not open latex file %s for writing: %s"), 199 $l2h_latex_file, $!)); 200 $status = 0; 201 return; 202 } 203 warn "# l2h: use ${l2h_latex_file} as latex file\n" if ($verbose); 204 print L2H_LATEX $l2h_latex_preamble; 205 } 206 # open the database that holds cached text 207 l2h_init_cache($self) if (!defined($self->get_conf('L2H_SKIP')) 208 or $self->get_conf('L2H_SKIP')); 209 210 my @replaced_commands = ('tex', 'math', 'displaymath'); 211 my $collected_commands = Texinfo::Common::collect_commands_in_tree($document_root, \@replaced_commands); 212 foreach my $command (@replaced_commands) { 213 ## we rely on @tex and @math being recorded as 'global commands' 214 #if ($self->{'extra'}->{$command}) { 215 if (scalar(@{$collected_commands->{$command}}) > 0) { 216 my $counter = 0; 217 #foreach my $root (@{$self->{'extra'}->{$command}}) { 218 foreach my $root (@{$collected_commands->{$command}}) { 219 $counter++; 220 my $tree; 221 if ($command eq 'math') { 222 $tree = $root->{'args'}->[0]; 223 } else { 224 $tree = {'contents' => [@{$root->{'contents'}}]}; 225 if ($tree->{'contents'}->[0] 226 and $tree->{'contents'}->[0]->{'type'} 227 and $tree->{'contents'}->[0]->{'type'} eq 'empty_line_after_command') { 228 shift @{$tree->{'contents'}}; 229 } 230 if ($tree->{'contents'}->[-1]->{'cmdname'} 231 and $tree->{'contents'}->[-1]->{'cmdname'} eq 'end') { 232 pop @{$tree->{'contents'}}; 233 } 234 } 235 my $text = Texinfo::Convert::Texinfo::convert($tree); 236 #$text .= "\n" if ($command eq 'tex'); 237 l2h_to_latex($self, $command, $text, $counter); 238 $commands_counters{$root} = $counter; 239 } 240 } 241 } 242 $status = l2h_finish_to_latex($self); 243 if ($status) { 244 $status = l2h_to_html($self); 245 } 246 if ($status) { 247 $status = l2h_init_from_html($self); 248 } 249 # FIXME use $status? That is abort when something goes wrong on the 250 # latex2html front? 251 return 1; 252} 253 254 255# print text (2nd arg) into latex file (if not already there nor in cache) 256# which can be later on replaced by the latex2html generated text. 257# 258sub l2h_to_latex($$$$) 259{ 260 my $self = shift; 261 my $command = shift; 262 my $text = shift; 263 my $counter = shift; 264 265 if ($command eq 'tex') { 266 $text .= ' '; 267 } elsif ($command eq 'math') { 268 $text = "\$".$text."\$"; 269 } elsif ($command eq 'displaymath') { 270 $text = "\$\$".$text."\$\$"; 271 } 272 $to_latex_count++; 273 $text =~ s/(\s*)$//; 274 # try whether we have text already on things to do 275 my $count = $l2h_to_latex{$text}; 276 unless ($count) { 277 $latex_count++; 278 $count = $latex_count; 279 # try whether we can get it from cache 280 my $cached_text = l2h_from_cache($text); 281 if (defined($cached_text)) { 282 $cached_count++; 283 # put the cached result in the html result array 284 $l2h_from_html[$count] = $cached_text; 285 } else { 286 $latex_converted_count++; 287 unless ($self->get_conf('L2H_SKIP')) { 288 print L2H_LATEX "\\begin{rawhtml}\n\n"; 289 print L2H_LATEX "<!-- l2h_begin $l2h_name $count -->\n"; 290 print L2H_LATEX "\\end{rawhtml}\n"; 291 292 print L2H_LATEX "$text\n"; 293 294 print L2H_LATEX "\\begin{rawhtml}\n"; 295 print L2H_LATEX "<!-- l2h_end $l2h_name $count -->\n\n"; 296 print L2H_LATEX "\\end{rawhtml}\n"; 297 } 298 } 299 $l2h_to_latex[$count] = $text; 300 $l2h_to_latex{$text} = $count; 301 } 302 $global_count{"${command}_$counter"} = $count; 303 return 1; 304} 305 306# print closing into latex file and close it 307sub l2h_finish_to_latex($) 308{ 309 my $self = shift; 310 my $reused = $to_latex_count - $latex_converted_count - $cached_count; 311 unless ($self->get_conf('L2H_SKIP')) { 312 print L2H_LATEX $l2h_latex_closing; 313 close (L2H_LATEX); 314 } 315 warn "# l2h: finished to latex ($cached_count cached, $reused reused, $latex_converted_count to process)\n" if ($verbose); 316 unless ($latex_count) { 317 # no @tex nor @math 318 l2h_finish($self); 319 return 0; 320 } 321 return 1; 322} 323 324################################### 325# Use latex2html to generate corresponding html code and images 326# 327# to_html([$l2h_latex_file, [$l2h_html_dir]]): 328# Call latex2html on $l2h_latex_file 329# Put images (prefixed with $l2h_name."_") and html file(s) in $l2h_html_dir 330# Return 1, on success 331# 0, otherwise 332# 333sub l2h_to_html($) 334{ 335 my $self = shift; 336 my ($call, $dotbug); 337 # when there are no tex constructs to convert (happens in case everything 338 # comes from the cache), there is no latex2html run 339 if ($self->get_conf('L2H_SKIP') or ($latex_converted_count == 0)) { 340 warn "# l2h: skipping latex2html run\n" if ($verbose); 341 return 1; 342 } 343 # Check for dot in directory where dvips will work 344 if ($self->get_conf('L2H_TMP')) { 345 if ($self->get_conf('L2H_TMP') =~ /\./) { 346 $self->document_warn(__("l2h: L2H_TMP directory contains a dot")); 347 $dotbug = 1; 348 } 349 } else { 350 if (cwd() =~ /\./) { 351 $self->document_warn(__("l2h: current directory contains a dot")); 352 $dotbug = 1; 353 } 354 } 355 return 0 if ($dotbug); 356 357 $call = $self->get_conf('L2H_L2H'); 358 # use init file, if specified 359 my $init_file = $self->get_conf('L2H_FILE'); 360 $call = $call . " -init_file " . $init_file 361 if (defined($init_file) and $init_file ne '' 362 and -f $init_file and -r $init_file); 363 # set output dir 364 $call .= (($docu_rdir ne '') ? " -dir $docu_rdir" : " -no_subdir"); 365 # use l2h_tmp, if specified 366 $call .= " -tmp ".$self->get_conf('L2H_TMP') 367 if (defined($self->get_conf('L2H_TMP')) 368 and $self->get_conf('L2H_TMP') ne ''); 369 # use a given html version if specified 370 $call .= " -html_version ".$self->get_conf('L2H_HTML_VERSION') 371 if (defined($self->get_conf('L2H_HTML_VERSION')) 372 and $self->get_conf('L2H_HTML_VERSION') ne ''); 373 # options we want to be sure of 374 $call .= " -address 0 -info 0 -split 0 -no_navigation -no_auto_link"; 375 $call .= " -prefix $l2h_prefix $l2h_latex_file"; 376 377 warn "# l2h: executing '$call'\n" if ($verbose); 378 if (system($call)) { 379 $self->document_error(sprintf(__("l2h: command did not succeed: %s"), 380 $call)); 381 return 0; 382 } else { 383 warn "# l2h: latex2html finished successfully\n" if ($verbose); 384 return 1; 385 } 386} 387 388########################## 389# Third stage: Extract generated contents from latex2html run 390# Initialize with: init_from_html 391# open $l2h_html_file for reading 392# reads in contents into array indexed by numbers 393# return 1, on success -- 0, otherwise 394# Finish with: finish 395# closes $l2h_html_dir/$l2h_name.".$docu_ext" 396 397 398# the images generated by latex2html have names like ${docu_name}_l2h_img?.png 399# they are copied to ${docu_name}_?.png, and html is changed accordingly. 400 401# FIXME is it really necessary to bother doing that? Looks like an unneeded 402# complication to me (pertusus, 2009), and it could go bad if there is some 403# SRC="(.*?)" in the text (though the regexp could be made more specific). 404 405# %l2h_img; # associate src file to destination file 406 # such that files are not copied twice 407sub l2h_change_image_file_names($$) 408{ 409 my $self = shift; 410 my $content = shift; 411 my @images = ($content =~ /SRC="(.*?)"/g); 412 my ($src, $dest); 413 414 for $src (@images) { 415 $dest = $l2h_img{$src}; 416 unless ($dest) { 417 my $ext = ''; 418 if ($src =~ /.*\.(.*)$/ and (!defined($self->get_conf('EXTENSION')) 419 or $1 ne $self->get_conf('EXTENSION'))) { 420 $ext = ".$1"; 421 } else { 422 # A warning when the image extension is the same than the 423 # document extension. copying the file could result in 424 # overwriting an output file (almost surely if the default 425 # texi2html file names are used). 426 $self->document_warn(sprintf(__( 427 "l2h: image has invalid extension: %s"), $src)); 428 next; 429 } 430 while (-e File::Spec->catpath($docu_volume, $docu_directories, 431 "${docu_name}_${image_count}$ext")) { 432 $image_count++; 433 } 434 my $file_src 435 = File::Spec->catpath($docu_volume, $docu_directories, $src); 436 $dest = "${docu_name}_${image_count}$ext"; 437 my $file_dest 438 = File::Spec->catpath($docu_volume, $docu_directories, $dest); 439 if ($debug) { 440 copy($file_src, $file_dest); 441 } else { 442 if (!rename($file_src, $file_dest)) { 443 $self->document_warn(sprintf(__("l2h: rename %s as %s failed: %s"), 444 $file_src, $file_dest, $!)); 445 } 446 } 447 $l2h_img{$src} = $dest; 448 } 449 $content =~ s/SRC="$src"/SRC="$dest"/g; 450 } 451 return $content; 452} 453 454sub l2h_init_from_html($) 455{ 456 my $self = shift; 457 # when there are no tex constructs to convert (happens in case everything 458 # comes from the cache), the html file that was generated by previous 459 # latex2html runs isn't reused. 460 if ($latex_converted_count == 0) { 461 return 1; 462 } 463 464 if (! open(L2H_HTML, "<$l2h_html_file")) { 465 $self->document_warn(sprintf(__("l2h: could not open %s: %s"), 466 $l2h_html_file, $!)); 467 return 0; 468 } 469 warn "# l2h: use $l2h_html_file as html file\n" if ($verbose); 470 471 my $html_converted_count = 0; # number of html resulting texts 472 # retrieved in the file 473 474 my ($count, $h_line); 475 while ($h_line = <L2H_HTML>) { 476 if ($h_line =~ /!-- l2h_begin $l2h_name ([0-9]+) --/) { 477 $count = $1; 478 my $h_content = ''; 479 my $h_end_found = 0; 480 while ($h_line = <L2H_HTML>) { 481 if ($h_line =~ /!-- l2h_end $l2h_name $count --/) { 482 $h_end_found = 1; 483 chomp $h_content; 484 chomp $h_content; 485 $html_converted_count++; 486 # transform image file names and copy image files 487 $h_content = l2h_change_image_file_names($self, $h_content); 488 # store result in the html result array 489 $l2h_from_html[$count] = $h_content; 490 # also add the result in cache hash 491 $l2h_cache{$l2h_to_latex[$count]} = $h_content; 492 last; 493 } 494 $h_content = $h_content.$h_line; 495 } 496 unless ($h_end_found) { 497 # couldn't found the closing comment. Should be a bug. 498 $self->document_warn(sprintf(__("latex2html.pm: end of \@%s item %d not found"), 499 $l2h_name, $count)); 500 close(L2H_HTML); 501 return 0; 502 } 503 } 504 } 505 506 # Not the same number of converted elements and retrieved elements 507 if ($latex_converted_count != $html_converted_count) { 508 $self->document_warn(sprintf(__( 509 "latex2html.pm: processing produced %d items in HTML; expected %d, the number of items found in the document"), 510 $html_converted_count, $latex_converted_count)); 511 } 512 513 warn "# l2h: Got $html_converted_count of $latex_count html contents\n" 514 if ($verbose); 515 516 close(L2H_HTML); 517 return 1; 518} 519 520# $html_output_count = 0; # html text outputed in html result file 521 522# called each time a construct handled by latex2html is encountered, should 523# output the corresponding html 524sub l2h_do_tex($$) 525{ 526 my $self = shift; 527 my $cmdname = shift;; 528 my $command = shift; 529 my $content = shift; 530 531 my $counter = $commands_counters{$command}; 532 return '' unless ($status); 533 my $count = $global_count{"${cmdname}_$counter"}; 534 ################################## begin debug section (incorrect counts) 535 if (!defined($count)) { 536 # counter is undefined 537 $invalid_counter_count++; 538 $self->document_warn( 539 sprintf(__("l2h: could not determine the fragment %d for \@%s"), 540 $counter, $cmdname)); 541 return ("<!-- l2h: ". __LINE__ . " undef count for ${cmdname}_$counter -->") 542 if ($debug); 543 return ''; 544 } elsif(($count <= 0) or ($count > $latex_count)) { 545 # counter out of range 546 $invalid_counter_count++; 547 $self->_bug_message("l2h: request of $count out of range [0,$latex_count]"); 548 return ("<!-- l2h: ". __LINE__ . " out of range count $count -->") 549 if ($debug); 550 return ''; 551 } 552 ################################## end debug section (incorrect counts) 553 554 # this seems to be a valid counter 555 my $result = ''; 556 $result = "<!-- l2h_begin $l2h_name $count -->" if ($debug); 557 if (defined($l2h_from_html[$count])) { 558 $html_output_count++; 559 $result .= $l2h_from_html[$count]; 560 $result .= "\n" if ($cmdname eq 'tex'); 561 } else { 562 # if the result is not in @l2h_from_html, there is an error somewhere. 563 $extract_error_count++; 564 $self->document_warn(sprintf(__( 565 "l2h: could not extract the fragment %d for \@%s with output counter %d from HTML"), 566 $counter, $cmdname, $count)); 567 # try simple (ordinary) substitution (without l2h) 568 $result .= "<!-- l2h: ". __LINE__ . " use default -->" if ($debug); 569 $result .= &{$self->default_commands_conversion($cmdname)}($self, 570 $cmdname, $command, $content); 571 } 572 $result .= "<!-- l2h_end $l2h_name $count -->" if ($debug); 573 return $result; 574} 575 576# store results in the cache and remove temporary files. 577sub l2h_finish($) 578{ 579 my $self = shift; 580 return 1 unless($status); 581 582 if ($verbose) { 583 if ($extract_error_count + $invalid_counter_count) { 584 warn "# l2h: finished from html ($extract_error_count extract and $invalid_counter_count invalid counter errors)\n"; 585 } else { 586 warn "# l2h: finished from html (no error)\n"; 587 } 588 if ($html_output_count != $latex_converted_count) { 589 # this may happen if @-commands are collected at some places 590 # but @-command at those places are not expanded later. For 591 # example @math on @multitable lines. 592 warn "# l2h: $html_output_count html outputed for $latex_converted_count converted\n"; 593 } 594 } 595 l2h_store_cache($self); 596 if ($self->get_conf('L2H_CLEAN')) { 597 warn "# l2h: removing temporary files generated by l2h extension\n" 598 if ($verbose); 599 my $quoted_l2h_name = quotemeta($l2h_name); 600 my $dir = $docu_rdir; 601 $dir = File::Spec->curdir() if ($dir eq ''); 602 if (opendir (DIR, $dir)) { 603 foreach my $file (grep { /^$quoted_l2h_name/ } readdir(DIR)) { 604 # FIXME error condition not checked 605 unlink File::Spec->catpath($docu_volume, $docu_directories, $file); 606 } 607 } 608 } 609 warn "# l2h: Finished\n" if $verbose; 610 return 1; 611} 612 613############################## 614# stuff for l2h caching 615# 616# FIXME it is clear that l2h stuff takes very long compared with texi2any 617# which is already quite long. However this also adds some complexity 618 619# I tried doing this with a dbm data base, but it did not store all 620# keys/values. Hence, I did as latex2html does it 621sub l2h_init_cache($) 622{ 623 my $self = shift; 624 if (-r $l2h_cache_file) { 625 my $rdo = do "$l2h_cache_file"; 626 $self->document_error(sprintf(__("l2h: could not load %s: %s"), 627 $l2h_cache_file, $@)) 628 unless ($rdo); 629 } 630} 631 632# store all the text obtained through latex2html 633sub l2h_store_cache($) 634{ 635 my $self = shift; 636 return unless $latex_count; 637 my ($key, $value); 638 unless (open(FH, ">$l2h_cache_file")) { 639 $self->document_error(sprintf(__("l2h: could not open %s for writing: %s"), 640 $l2h_cache_file, $!)); 641 return; 642 } 643 foreach my $key(sort(keys(%l2h_cache))) { 644 #while (($key, $value) = each %l2h_cache) { 645 my $value = $l2h_cache{$key}; 646 # escape stuff 647 $key =~ s|/|\\/|g; 648 $key =~ s|\\\\/|\\/|g; 649 # weird, a \ at the end of the key results in an error 650 # maybe this also broke the dbm database stuff 651 $key =~ s|\\$|\\\\|; 652 $value =~ s/\|/\\\|/go; 653 $value =~ s/\\\\\|/\\\|/go; 654 $value =~ s|\\\\|\\\\\\\\|g; 655 print FH "\n\$l2h_cache_key = q/$key/;\n"; 656 print FH "\$l2h_cache{\$l2h_cache_key} = q|$value|;\n"; 657 } 658 print FH "1;"; 659 close (FH); 660} 661 662# return cached html, if it exists for text, and if all pictures 663# are there, as well 664sub l2h_from_cache($) 665{ 666 my $text = shift; 667 my $cached = $l2h_cache{$text}; 668 if (defined($cached)) { 669 while ($cached =~ m/SRC="(.*?)"/g) { 670 unless (-e File::Spec->catpath($docu_volume, $docu_directories, $1)) { 671 return undef; 672 } 673 } 674 return $cached; 675 } 676 return undef; 677} 678 6791; 680