1# This module does multiple indices, supporting the style of the LaTex 'index' 2# package. 3 4# Version Information: 5# 16-Feb-2005 -- Original Creation. Karl E. Cunningham 6# 14-Mar-2005 -- Clarified and Consolodated some of the code. 7# Changed to smoothly handle single and multiple indices. 8 9# Two LaTeX index formats are supported... 10# --- SINGLE INDEX --- 11# \usepackage{makeidx} 12# \makeindex 13# \index{entry1} 14# \index{entry2} 15# \index{entry3} 16# ... 17# \printindex 18# 19# --- MULTIPLE INDICES --- 20# 21# \usepackage{makeidx} 22# \usepackage{index} 23# \makeindex -- latex2html doesn't care but LaTeX does. 24# \newindex{ref1}{ext1}{ext2}{title1} 25# \newindex{ref2}{ext1}{ext2}{title2} 26# \newindex{ref3}{ext1}{ext2}{title3} 27# \index[ref1]{entry1} 28# \index[ref1]{entry2} 29# \index[ref3]{entry3} 30# \index[ref2]{entry4} 31# \index{entry5} 32# \index[ref3]{entry6} 33# ... 34# \printindex[ref1] 35# \printindex[ref2] 36# \printindex[ref3] 37# \printindex 38# ___________________ 39# 40# For the multiple-index style, each index is identified by the ref argument to \newindex, \index, 41# and \printindex. A default index is allowed, which is indicated by omitting the optional 42# argument. The default index does not require a \newindex command. As \index commands 43# are encountered, their entries are stored according 44# to the ref argument. When the \printindex command is encountered, the stored index 45# entries for that argument are retrieved and printed. The title for each index is taken 46# from the last argument in the \newindex command. 47# While processing \index and \printindex commands, if no argument is given the index entries 48# are built into a default index. The title of the default index is simply "Index". 49# This makes the difference between single- and multiple-index processing trivial. 50# 51# Another method can be used by omitting the \printindex command and just using \include to 52# pull in index files created by the makeindex program. These files will start with 53# \begin{theindex}. This command is used to determine where to print the index. Using this 54# approach, the indices will be output in the same order as the newindex commands were 55# originally found (see below). Using a combination of \printindex and \include{indexfile} has not 56# been tested and may produce undesireable results. 57# 58# The index data are stored in a hash for later sorting and output. As \printindex 59# commands are handled, the order in which they were found in the tex filea is saved, 60# associated with the ref argument to \printindex. 61# 62# We use the original %index hash to store the index data into. We append a \002 followed by the 63# name of the index to isolate the entries in different indices from each other. This is necessary 64# so that different indices can have entries with the same name. For the default index, the \002 is 65# appended without the name. 66# 67# Since the index order in the output cannot be determined if the \include{indexfile} 68# command is used, the order will be assumed from the order in which the \newindex 69# commands were originally seen in the TeX files. This order is saved as well as the 70# order determined from any printindex{ref} commands. If \printindex commnads are used 71# to specify the index output, that order will be used. If the \include{idxfile} command 72# is used, the order of the original newindex commands will be used. In this case the 73# default index will be printed last since it doesn't have a corresponding \newindex 74# command and its order cannot be determined. Mixing \printindex and \include{idxfile} 75# commands in the same file is likely to produce less than satisfactory results. 76# 77# 78# The hash containing index data is named %indices. It contains the following data: 79#{ 80# 'title' => { 81# $ref1 => $indextitle , 82# $ref2 => $indextitle , 83# ... 84# }, 85# 'newcmdorder' => [ ref1, ref2, ..., * ], # asterisk indicates the position of the default index. 86# 'printindorder' => [ ref1, ref2, ..., * ], # asterisk indicates the position of the default index. 87#} 88 89 90# Globals to handle multiple indices. 91my %indices; 92 93# This tells the system to use up to 7 words in index entries. 94$WORDS_IN_INDEX = 10; 95 96# KEC 2-18-05 97# Handles the \newindex command. This is called if the \newindex command is 98# encountered in the LaTex source. Gets the index ref and title from the arguments. 99# Saves the index ref and title. 100# Note that we are called once to handle multiple \newindex commands that are 101# newline-separated. 102sub do_cmd_newindex { 103 my $data = shift; 104 # The data is sent to us as fields delimited by their ID #'s. We extract the 105 # fields. 106 foreach my $line (split("\n",$data)) { 107 my @fields = split (/(?:\<\#\d+?\#\>)+/,$line); 108 109 # The index name and title are the second and fourth fields in the data. 110 if ($line =~ /^</ or $line =~ /^\\newindex/) { 111 my ($indexref,$indextitle) = ($fields[1],$fields[4]); 112 $indices{'title'}{$indexref} = $indextitle; 113 push (@{$indices{'newcmdorder'}},$indexref); 114 } 115 } 116} 117 118 119# KEC -- Copied from makeidx.perl and modified to do multiple indices. 120# Processes an \index entry from the LaTex file. 121# Gets the optional argument from the index command, which is the name of the index 122# into which to place the entry. 123# Drops the brackets from the index_name 124# Puts the index entry into the html stream 125# Creates the tokenized index entry (which also saves the index entry info 126sub do_real_index { 127 local($_) = @_; 128 local($pat,$idx_entry,$index_name); 129 # catches opt-arg from \index commands for index.sty 130 $index_name = &get_next_optional_argument; 131 $index_name = "" unless defined $index_name; 132 # Drop leading and trailing brackets from the index name. 133 $index_name =~ s/^\[|\]$//g; 134 135 $idx_entry = &missing_braces unless ( 136 (s/$next_pair_pr_rx/$pat=$1;$idx_entry=$2;''/e) 137 ||(s/$next_pair_rx/$pat=$1;$idx_entry=$2;''/e)); 138 139 if ($index_name and defined $idx_entry and 140 !defined $indices{'title'}{$index_name}) { 141 print STDERR "\nInvalid Index Name: \\index \[$index_name\]\{$idx_entry\}\n"; 142 } 143 144 $idx_entry = &named_index_entry($pat, $idx_entry,$index_name); 145 $idx_entry.$_; 146} 147 148# Creates and saves an index entry in the index hashes. 149# Modified to do multiple indices. 150# Creates an index_key that allows index entries to have the same characteristics but be in 151# different indices. This index_key is the regular key with the index name appended. 152# Save the index order for the entry in the %index_order hash. 153sub named_index_entry { 154 local($br_id, $str, $index_name) = @_; 155 my ($index_key); 156 # escape the quoting etc characters 157 # ! -> \001 158 # @ -> \002 159 # | -> \003 160 $* = 1; $str =~ s/\n\s*/ /g; $* = 0; # remove any newlines 161 # protect \001 occurring with images 162 $str =~ s/\001/\016/g; # 0x1 to 0xF 163 $str =~ s/\\\\/\011/g; # Double backslash -> 0xB 164 $str =~ s/\\;SPMquot;/\012/g; # \;SPMquot; -> 0xC 165 $str =~ s/;SPMquot;!/\013/g; # ;SPMquot; -> 0xD 166 $str =~ s/!/\001/g; # Exclamation point -> 0x1 167 $str =~ s/\013/!/g; # 0xD -> Exclaimation point 168 $str =~ s/;SPMquot;@/\015/g; # ;SPMquot;@ to 0xF 169 $str =~ s/@/\002/g; # At sign -> 0x2 170 $str =~ s/\015/@/g; # 0xF to At sign 171 $str =~ s/;SPMquot;\|/\017/g; # ;SMPquot;| to 0x11 172 $str =~ s/\|/\003/g; # Vertical line to 0x3 173 $str =~ s/\017/|/g; # 0x11 to vertical line 174 $str =~ s/;SPMquot;(.)/\1/g; # ;SPMquot; -> whatever the next character is 175 $str =~ s/\012/;SPMquot;/g; # 0x12 to ;SPMquot; 176 $str =~ s/\011/\\\\/g; # 0x11 to double backslash 177 local($key_part, $pageref) = split("\003", $str, 2); 178 179 # For any keys of the form: blablabla!blablabla, which want to be split at the 180 # exclamation point, replace the ! with a comma and a space. We don't do it 181 # that way for this index. 182 $key_part =~ s/\001/, /g; 183 local(@keys) = split("\001", $key_part); 184 # If TITLE is not yet available use $before. 185 $TITLE = $saved_title if (($saved_title)&&(!($TITLE)||($TITLE eq $default_title))); 186 $TITLE = $before unless $TITLE; 187 # Save the reference 188 local($words) = ''; 189 if ($SHOW_SECTION_NUMBERS) { $words = &make_idxnum; } 190 elsif ($SHORT_INDEX) { $words = &make_shortidxname; } 191 else { $words = &make_idxname; } 192 local($super_key) = ''; 193 local($sort_key, $printable_key, $cur_key); 194 foreach $key (@keys) { 195 $key =~ s/\016/\001/g; # revert protected \001s 196 ($sort_key, $printable_key) = split("\002", $key); 197 # 198 # RRM: 16 May 1996 199 # any \label in the printable-key will have already 200 # created a label where the \index occurred. 201 # This has to be removed, so that the desired label 202 # will be found on the Index page instead. 203 # 204 if ($printable_key =~ /tex2html_anchor_mark/ ) { 205 $printable_key =~ s/><tex2html_anchor_mark><\/A><A//g; 206 local($tmpA,$tmpB) = split("NAME=\"", $printable_key); 207 ($tmpA,$tmpB) = split("\"", $tmpB); 208 $ref_files{$tmpA}=''; 209 $index_labels{$tmpA} = 1; 210 } 211 # 212 # resolve and clean-up the hyperlink index-entries 213 # so they can be saved in an index.pl file 214 # 215 if ($printable_key =~ /$cross_ref_mark/ ) { 216 local($label,$id,$ref_label); 217 # $printable_key =~ s/$cross_ref_mark#(\w+)#(\w+)>$cross_ref_mark/ 218 $printable_key =~ s/$cross_ref_mark#([^#]+)#([^>]+)>$cross_ref_mark/ 219 do { ($label,$id) = ($1,$2); 220 $ref_label = $external_labels{$label} unless 221 ($ref_label = $ref_files{$label}); 222 '"' . "$ref_label#$label" . '">' . 223 &get_ref_mark($label,$id)} 224 /geo; 225 } 226 $printable_key =~ s/<\#[^\#>]*\#>//go; 227 #RRM 228 # recognise \char combinations, for a \backslash 229 # 230 $printable_key =~ s/\&\#;\'134/\\/g; # restore \\s 231 $printable_key =~ s/\&\#;\`<BR> /\\/g; # ditto 232 $printable_key =~ s/\&\#;*SPMquot;92/\\/g; # ditto 233 # 234 # $sort_key .= "@$printable_key" if !($printable_key); # RRM 235 $sort_key .= "@$printable_key" if !($sort_key); # RRM 236 $sort_key =~ tr/A-Z/a-z/; 237 if ($super_key) { 238 $cur_key = $super_key . "\001" . $sort_key; 239 $sub_index{$super_key} .= $cur_key . "\004"; 240 } else { 241 $cur_key = $sort_key; 242 } 243 244 # Append the $index_name to the current key with a \002 delimiter. This will 245 # allow the same index entry to appear in more than one index. 246 $index_key = $cur_key . "\002$index_name"; 247 248 $index{$index_key} .= ""; 249 250 # 251 # RRM, 15 June 1996 252 # if there is no printable key, but one is known from 253 # a previous index-entry, then use it. 254 # 255 if (!($printable_key) && ($printable_key{$index_key})) 256 { $printable_key = $printable_key{$index_key}; } 257# if (!($printable_key) && ($printable_key{$cur_key})) 258# { $printable_key = $printable_key{$cur_key}; } 259 # 260 # do not overwrite the printable_key if it contains an anchor 261 # 262 if (!($printable_key{$index_key} =~ /tex2html_anchor_mark/ )) 263 { $printable_key{$index_key} = $printable_key || $key; } 264# if (!($printable_key{$cur_key} =~ /tex2html_anchor_mark/ )) 265# { $printable_key{$cur_key} = $printable_key || $key; } 266 267 $super_key = $cur_key; 268 } 269 # 270 # RRM 271 # page-ranges, from |( and |) and |see 272 # 273 if ($pageref) { 274 if ($pageref eq "\(" ) { 275 $pageref = ''; 276 $next .= " from "; 277 } elsif ($pageref eq "\)" ) { 278 $pageref = ''; 279 local($next) = $index{$index_key}; 280# local($next) = $index{$cur_key}; 281 # $next =~ s/[\|] *$//; 282 $next =~ s/(\n )?\| $//; 283 $index{$index_key} = "$next to "; 284# $index{$cur_key} = "$next to "; 285 } 286 } 287 288 if ($pageref) { 289 $pageref =~ s/\s*$//g; # remove trailing spaces 290 if (!$pageref) { $pageref = ' ' } 291 $pageref =~ s/see/<i>see <\/i> /g; 292 # 293 # RRM: 27 Dec 1996 294 # check if $pageref corresponds to a style command. 295 # If so, apply it to the $words. 296 # 297 local($tmp) = "do_cmd_$pageref"; 298 if (defined &$tmp) { 299 $words = &$tmp("<#0#>$words<#0#>"); 300 $words =~ s/<\#[^\#]*\#>//go; 301 $pageref = ''; 302 } 303 } 304 # 305 # RRM: 25 May 1996 306 # any \label in the pageref section will have already 307 # created a label where the \index occurred. 308 # This has to be removed, so that the desired label 309 # will be found on the Index page instead. 310 # 311 if ($pageref) { 312 if ($pageref =~ /tex2html_anchor_mark/ ) { 313 $pageref =~ s/><tex2html_anchor_mark><\/A><A//g; 314 local($tmpA,$tmpB) = split("NAME=\"", $pageref); 315 ($tmpA,$tmpB) = split("\"", $tmpB); 316 $ref_files{$tmpA}=''; 317 $index_labels{$tmpA} = 1; 318 } 319 # 320 # resolve and clean-up any hyperlinks in the page-ref, 321 # so they can be saved in an index.pl file 322 # 323 if ($pageref =~ /$cross_ref_mark/ ) { 324 local($label,$id,$ref_label); 325 # $pageref =~ s/$cross_ref_mark#(\w+)#(\w+)>$cross_ref_mark/ 326 $pageref =~ s/$cross_ref_mark#([^#]+)#([^>]+)>$cross_ref_mark/ 327 do { ($label,$id) = ($1,$2); 328 $ref_files{$label} = ''; # ???? RRM 329 if ($index_labels{$label}) { $ref_label = ''; } 330 else { $ref_label = $external_labels{$label} 331 unless ($ref_label = $ref_files{$label}); 332 } 333 '"' . "$ref_label#$label" . '">' . &get_ref_mark($label,$id)}/geo; 334 } 335 $pageref =~ s/<\#[^\#>]*\#>//go; 336 337 if ($pageref eq ' ') { $index{$index_key}='@'; } 338 else { $index{$index_key} .= $pageref . "\n | "; } 339 } else { 340 local($thisref) = &make_named_href('',"$CURRENT_FILE#$br_id",$words); 341 $thisref =~ s/\n//g; 342 $index{$index_key} .= $thisref."\n | "; 343 } 344 #print "\nREF: $sort_key : $index_key :$index{$index_key}"; 345 346 #join('',"<A NAME=$br_id>$anchor_invisible_mark<\/A>",$_); 347 348 "<A NAME=\"$br_id\">$anchor_invisible_mark<\/A>"; 349} 350 351 352# KEC. -- Copied from makeidx.perl, then modified to do multiple indices. 353# Feeds the index entries to the output. This is called for each index to be built. 354# 355# Generates a list of lookup keys for index entries, from both %printable_keys 356# and %index keys. 357# Sorts the keys according to index-sorting rules. 358# Removes keys with a 0x01 token. (duplicates?) 359# Builds a string to go to the index file. 360# Adds the index entries to the string if they belong in this index. 361# Keeps track of which index is being worked on, so only the proper entries 362# are included. 363# Places the index just built in to the output at the proper place. 364{ my $index_number = 0; 365sub add_real_idx { 366 print "\nDoing the index ... Index Number $index_number\n"; 367 local($key, @keys, $next, $index, $old_key, $old_html); 368 my ($idx_ref,$keyref); 369 # RRM, 15.6.96: index constructed from %printable_key, not %index 370 @keys = keys %printable_key; 371 372 while (/$idx_mark/) { 373 # Get the index reference from what follows the $idx_mark and 374 # remove it from the string. 375 s/$idxmark\002(.*?)\002/$idxmark/; 376 $idx_ref = $1; 377 $index = ''; 378 # include non- makeidx index-entries 379 foreach $key (keys %index) { 380 next if $printable_key{$key}; 381 $old_key = $key; 382 if ($key =~ s/###(.*)$//) { 383 next if $printable_key{$key}; 384 push (@keys, $key); 385 $printable_key{$key} = $key; 386 if ($index{$old_key} =~ /HREF="([^"]*)"/i) { 387 $old_html = $1; 388 $old_html =~ /$dd?([^#\Q$dd\E]*)#/; 389 $old_html = $1; 390 } else { $old_html = '' } 391 $index{$key} = $index{$old_key} . $old_html."</A>\n | "; 392 }; 393 } 394 @keys = sort makeidx_keysort @keys; 395 @keys = grep(!/\001/, @keys); 396 my $cnt = 0; 397 foreach $key (@keys) { 398 my ($keyref) = $key =~ /.*\002(.*)/; 399 next unless ($idx_ref eq $keyref); # KEC. 400 $index .= &add_idx_key($key); 401 $cnt++; 402 } 403 print "$cnt Index Entries Added\n"; 404 $index = '<DD>'.$index unless ($index =~ /^\s*<D(D|T)>/); 405 $index_number++; # KEC. 406 if ($SHORT_INDEX) { 407 print "(compact version with Legend)"; 408 local($num) = ( $index =~ s/\<D/<D/g ); 409 if ($num > 50 ) { 410 s/$idx_mark/$preindex<HR><DL>\n$index\n<\/DL>$preindex/o; 411 } else { 412 s/$idx_mark/$preindex<HR><DL>\n$index\n<\/DL>/o; 413 } 414 } else { 415 s/$idx_mark/<DL COMPACT>\n$index\n<\/DL>/o; } 416 } 417} 418} 419 420# KEC. Copied from latex2html.pl and modified to support multiple indices. 421# The bibliography and the index should be treated as separate sections 422# in their own HTML files. The \bibliography{} command acts as a sectioning command 423# that has the desired effect. But when the bibliography is constructed 424# manually using the thebibliography environment, or when using the 425# theindex environment it is not possible to use the normal sectioning 426# mechanism. This subroutine inserts a \bibliography{} or a dummy 427# \textohtmlindex command just before the appropriate environments 428# to force sectioning. 429sub add_bbl_and_idx_dummy_commands { 430 local($id) = $global{'max_id'}; 431 432 s/([\\]begin\s*$O\d+$C\s*thebibliography)/$bbl_cnt++; $1/eg; 433 ## if ($bbl_cnt == 1) { 434 s/([\\]begin\s*$O\d+$C\s*thebibliography)/$id++; "\\bibliography$O$id$C$O$id$C $1"/geo; 435 #} 436 $global{'max_id'} = $id; 437 # KEC. Modified to global substitution to place multiple index tokens. 438 s/[\\]begin\s*($O\d+$C)\s*theindex/\\textohtmlindex$1/go; 439 # KEC. Modified to pick up the optional argument to \printindex 440 s/[\\]printindex\s*(\[.*?\])?/ 441 do { (defined $1) ? "\\textohtmlindex $1" : "\\textohtmlindex []"; } /ego; 442 &lib_add_bbl_and_idx_dummy_commands() if defined(&lib_add_bbl_and_idx_dummy_commands); 443} 444 445# KEC. Copied from latex2html.pl and modified to support multiple indices. 446# For each textohtmlindex mark found, determine the index titles and headers. 447# We place the index ref in the header so the proper index can be generated later. 448# For the default index, the index ref is blank. 449# 450# One problem is that this routine is called twice.. Once for processing the 451# command as originally seen, and once for processing the command when 452# doing the name for the index file. We can detect that by looking at the 453# id numbers (or ref) surrounding the \theindex command, and not incrementing 454# index_number unless a new id (or ref) is seen. This has the side effect of 455# having to unconventionally start the index_number at -1. But it works. 456# 457# Gets the title from the list of indices. 458# If this is the first index, save the title in $first_idx_file. This is what's referenced 459# in the navigation buttons. 460# Increment the index_number for next time. 461# If the indexname command is defined or a newcommand defined for indexname, do it. 462# Save the index TITLE in the toc 463# Save the first_idx_file into the idxfile. This goes into the nav buttons. 464# Build index_labels if needed. 465# Create the index headings and put them in the output stream. 466 467{ my $index_number = 0; # Will be incremented before use. 468 my $first_idx_file; # Static 469 my $no_increment = 0; 470 471sub do_cmd_textohtmlindex { 472 local($_) = @_; 473 my ($idxref,$idxnum,$index_name); 474 475 # We get called from make_name with the first argument = "\001noincrement". This is a sign 476 # to not increment $index_number the next time we are called. We get called twice, once 477 # my make_name and once by process_command. Unfortunately, make_name calls us just to set the name 478 # but doesn't use the result so we get called a second time by process_command. This works fine 479 # except for cases where there are multiple indices except if they aren't named, which is the case 480 # when the index is inserted by an include command in latex. In these cases we are only able to use 481 # the index number to decide which index to draw from, and we don't know how to increment that index 482 # number if we get called a variable number of times for the same index, as is the case between 483 # making html (one output file) and web (multiple output files) output formats. 484 if (/\001noincrement/) { 485 $no_increment = 1; 486 return; 487 } 488 489 # Remove (but save) the index reference 490 s/^\s*\[(.*?)\]/{$idxref = $1; "";}/e; 491 492 # If we have an $idxref, the index name was specified. In this case, we have all the 493 # information we need to carry on. Otherwise, we need to get the idxref 494 # from the $index_number and set the name to "Index". 495 if ($idxref) { 496 $index_name = $indices{'title'}{$idxref}; 497 } else { 498 if (defined ($idxref = $indices{'newcmdorder'}->[$index_number])) { 499 $index_name = $indices{'title'}{$idxref}; 500 } else { 501 $idxref = ''; 502 $index_name = "Index"; 503 } 504 } 505 506 $idx_title = "Index"; # The name displayed in the nav bar text. 507 508 # Only set $idxfile if we are at the first index. This will point the 509 # navigation panel to the first index file rather than the last. 510 $first_idx_file = $CURRENT_FILE if ($index_number == 0); 511 $idxfile = $first_idx_file; # Pointer for the Index button in the nav bar. 512 $toc_sec_title = $index_name; # Index link text in the toc. 513 $TITLE = $toc_sec_title; # Title for this index, from which its filename is built. 514 if (%index_labels) { &make_index_labels(); } 515 if (($SHORT_INDEX) && (%index_segment)) { &make_preindex(); } 516 else { $preindex = ''; } 517 local $idx_head = $section_headings{'textohtmlindex'}; 518 local($heading) = join('' 519 , &make_section_heading($TITLE, $idx_head) 520 , $idx_mark, "\002", $idxref, "\002" ); 521 local($pre,$post) = &minimize_open_tags($heading); 522 $index_number++ unless ($no_increment); 523 $no_increment = 0; 524 join('',"<BR>\n" , $pre, $_); 525} 526} 527 528# Returns an index key, given the key passed as the first argument. 529# Not modified for multiple indices. 530sub add_idx_key { 531 local($key) = @_; 532 local($index, $next); 533 if (($index{$key} eq '@' )&&(!($index_printed{$key}))) { 534 if ($SHORT_INDEX) { $index .= "<DD><BR>\n<DT>".&print_key."\n<DD>"; } 535 else { $index .= "<DT><DD><BR>\n<DT>".&print_key."\n<DD>"; } 536 } elsif (($index{$key})&&(!($index_printed{$key}))) { 537 if ($SHORT_INDEX) { 538 $next = "<DD>".&print_key."\n : ". &print_idx_links; 539 } else { 540 $next = "<DT>".&print_key."\n<DD>". &print_idx_links; 541 } 542 $index .= $next."\n"; 543 $index_printed{$key} = 1; 544 } 545 546 if ($sub_index{$key}) { 547 local($subkey, @subkeys, $subnext, $subindex); 548 @subkeys = sort(split("\004", $sub_index{$key})); 549 if ($SHORT_INDEX) { 550 $index .= "<DD>".&print_key unless $index_printed{$key}; 551 $index .= "<DL>\n"; 552 } else { 553 $index .= "<DT>".&print_key."\n<DD>" unless $index_printed{$key}; 554 $index .= "<DL COMPACT>\n"; 555 } 556 foreach $subkey (@subkeys) { 557 $index .= &add_sub_idx_key($subkey) unless ($index_printed{$subkey}); 558 } 559 $index .= "</DL>\n"; 560 } 561 return $index; 562} 563 5641; # Must be present as the last line. 565