#!/usr/bin/perl -w # # mkprevnext # $Id: mkprevnext,v 1.22 2007/02/23 05:15:17 johnh Exp $ # # Copyright (C) 1994-1996,2012 Free Software Foundation, Inc. # Comments to . # # This file is under the Gnu Public License. # sub usage { print STDOUT <) { chomp; &reindex_file($_); } }; exit 0; # # Read the index file. # Build links of in $index{"$file#$subject","$prevnext"}. # Assumes that the index is sorted. # sub read_index { local($indexfile) = @_; local ($file, $subject); local (@old_sort_order, @sort_order); local($filesubject, $prevfilesubject) = ('', ''); if (-z $indexfile) { warn("$0: aborted. $indexfile is zero length.\n"); exit 0; }; open(INDEX,"<$indexfile") || die("Cannot open $indexfile"); binmode INDEX; ($prevurl, $prevfile, $prevsubject) = ("", "", ""); @sort_order = ("") x 3; while () { chop if (/\n$/); $url = $_; ($filehead, $file, $subject) = /^(.*)\/([^#]*)\#(.*)$/; # Sigh, have to fold things to upper case since sort only # does that, not to lower case. $filehead = uc($filehead); $file = uc($file); $subject = uc($subject); $filesubject = "$file#$subject"; # verification die ("Bad index entry: $_") if (!defined($file) || !defined($subject)); @old_sort_order = @sort_order; @sort_order = ($subject, $filehead, $file); foreach $i (0..$#sort_order) { last if ($sort_order[$i] gt $old_sort_order[$i]); die ("Index is not in sorted order (entries $i).\n\t$sort_order[$i]\n\t$old_sort_order[$i]\n") if ($sort_order[$i] lt $old_sort_order[$i]) }; # Skip repeated entries. if ($filesubject eq $prevfilesubject) { $count_i{$filesubject}++; next; }; # Record the links. $url_i{$filesubject} = $url; if ($prevsubject eq $subject) { $link_i{$filesubject,'prev'} = $prevfilesubject; $link_i{$prevfilesubject,'next'} = $filesubject; } else { $link_i{$filesubject,'prev'} = 'none'; $link_i{$prevfilesubject,'next'} = 'none'; }; # Count entries per-file. $count_i{$filesubject} = 1; ($prevurl, $prevfile, $prevsubject, $prevfilesubject) = ($url, $file, $subject, $filesubject); }; # Close the last pointer and hacks for null pointers. $link_i{$prevfilesubject,'next'} = 'none'; $url_i{'none'} = 'none'; $count_i{'none'} = 1; close (INDEX); } # # Go through a particular file # and update its prev/next pointers. # sub reindex_file { local ($fullfile) = @_; local (@data, $change, $mode, $subject); local ($mode_lookheader, $mode_expectdash, $mode_expectprev, $mode_expectnext) = (0..99); local(@olddata); local (@data, $data, $error); local ($subject_length, $found_expected_label); local (%subject_count) = (); local($file) = ($fullfile); $file =~ s@.*/([^/]+)@$1@; # basename open(FILE,"<$fullfile") || die("Cannot open $file"); @olddata = ; close(FILE); # $file = uc($file); $change = 0; $mode = $mode_lookheader; # # Scan through the file, looking for headers. # There is some context senstivity using $mode. # foreach (@olddata) { if ($mode == $mode_lookheader) { if (!/^(\* .*)$/) { # skip simple data push (@data, $_); next; } else { # header $subject = uc($1); $filesubject = "$file#$subject"; push (@data, $_); $subject_length = length($_) - 1; $subject_count{$subject}++; $mode = $mode_expectdash; next; }; } elsif ($mode == $mode_expectdash) { if (/^\-+$/) { # Check and fix dash length. if (length($_)-1 != $subject_length) { $_ = ("-" x $subject_length) . "\n"; $change++; }; push (@data, $_); $mode = $mode_expectprev; next; } else { # warn("warning: subject <$subject> missing dashes in $file.\n") if (!/^\*/); push (@data, $_); $mode = $mode_lookheader; next; }; } elsif ($mode == $mode_expectprev) { $found_expected_label = (/^prev: \<(.*)\>$/) ? 1 : 0; push (@data, &new_link('prev', $file, $subject, $subject_count{$subject})); $change++ if (!$found_expected_label || ($found_expected_label && $data[$#data] ne $_)); $mode = $mode_expectnext; if ($found_expected_label) { next; } else { redo; }; } elsif ($mode == $mode_expectnext) { $found_expected_label = (/^next: \<(.*)\>$/) ? 1 : 0; push (@data, &new_link('next', $file, $subject, $subject_count{$subject})); $change++ if (!$found_expected_label || ($found_expected_label && $data[$#data] ne $_)); $mode = $mode_lookheader; if ($found_expected_label) { next; } else { redo; }; } else { die ("bad mode: $mode"); }; die("end of loop reached unexpectedly."); }; close (FILE); return if (!$change); warn("Updating file $file.\n") if ($verbose); warn(" writing backup file ${fullfile}~.\n") if ($verbose); open(BFILE, ">$fullfile~") || die("Cannot write backup file $fullfile~.\n"); $data = join("", @olddata); $error = syswrite(BFILE, $data, length($data)); die("Backup file failed.\n") unless ($error = length($data)); open (FILE, ">$fullfile") || goto abort; $data = join("", @data); $error = syswrite(FILE, $data, length($data)); goto abort unless ($error == length($data)); close (FILE) || goto abort; return; abort: close (FILE); # ignore error warn ("Aborting changes to file $file.\n"); rename("$fullfile~", "$fullfile") || die("Could not back-out changes to $file. Old data saved in $file~."); return; } sub new_link { local ($direction, $file, $subject, $srcposition) = @_; local($filesubject) = "$file#$subject"; local($other_count); # First handle ignorance. return &format_url($direction,'none') if (!defined($link_i{$filesubject,$direction})); # See if we're in the same file. if (($direction eq 'prev' && $srcposition > 1) || ($direction eq 'next' && $srcposition < $count_i{$filesubject})) { return &format_url($direction, $url_i{$filesubject}, $srcposition + $direction_delta{$direction}); } else { # In a different file. Does the other file have multple entries? $other_count = $count_i{ $link_i{$filesubject,$direction} }; if ($other_count != 1) { return &format_url($direction, $url_i{ $link_i{$filesubject,$direction} }, ( $direction eq 'prev' ? $other_count : 1)); } else { # Different file with only one entry. return &format_url($direction, $url_i{ $link_i{$filesubject,$direction} } ); }; }; } sub format_url { local($direction, $url, $count) = @_; $url =~ s/\#\*/#$count*/ if (defined($count)); return "$direction: <$url>\n"; } ## substutite for "uc", if you want to back-port to perl4. # sub tolower { # local ($s) = @_; # $s =~ tr/a-z/A-Z/; # return $s; # }