#!/usr/bin/perl -w # This script takes a number of files containing a recipe name, # and the index tags found in that recipe, and generates an index # page for the collection of pages. # It is meant for use in conjunction with an XSL script which # generates the files containing just the recipe name and index tags # (each alone on a separate line) from the XHTML versions of the recipes. my $bookindexname = shift; my @index = (); my @sortedindex = (); my $todaysdate = `date +"%B %e, %G"`; chomp $todaysdate; my $filename; my $recipename; my $targetname; while ( defined $ARGV[0] ) { # Get the name of the next file, and open it my $filename = shift; open( IDX, "< $filename" ) || die "can't open $filename: $!"; # Generate the target name (ignore any suffix) $filename =~ m/(.+)\..*/; $targetname = $1 . '.html'; # Get the recipe name $recipename = ''; while( ) { if( m/(.+)/ ) { $recipename = $1; } last if $recipename ne ''; } if( $recipename eq '' ) { die "no recipe name found in $filename"; } # Place each entry in the file into an array of index entries while( ) { m/(.+)\s*/; if( $1 ne ' ' ) { $entry = {}; $entry->{name} = $1; $entry->{recipe} = $recipename; $entry->{target} = $targetname; push( @index, $entry ); } } close IDX; } # This is the function used to compare entries sub indexwise { $a->{name} cmp $b->{name} } # Now sort the array @sortedindex = sort indexwise @index; # Backup any existing output file my $backupindexname = $bookindexname . '.bak'; if( -e $backupindexname ) { system( "rm -f $backupindexname" ) } if( -e $bookindexname ) { rename( $bookindexname, $backupindexname ) || die "can't rename $bookindexname to $backupindexname: $!"; } # And generate the new output file open( NEW, "> $bookindexname" ) || die "can't open $bookindexname: $!"; # Print the header print NEW qq( Watlington Family Recipes: Index of Recipes and Ingredients

Index of Recipes and Ingredients

    ); # Print the entries # # This is more complicated than it originally seemed, as index entries # are combined and formatted together in several cases. This is modeled # as a state machine, with four states: normal, multiple, subentry, and # multsubentry. The state transition diagram is the following: # # state == normal and # MajorKey1 followed by # MajorKey2 # or MajorKey2!MinorKey1 # yields: # nextstate == normal #
  • MajorKey1 # Recipe1
  • # # state == normal and # MajorKey1 followed by # MajorKey1 # yields: # nextstate == multiple #
  • MajorKey1 # Recipe1, # (note no final
  • ) # # state == normal and # MajorKey1!MinorKey1 followed by # MajorKey1!MinorKey2 # yields: # nextstate == subentry #
  • MajorKey1 # or
  • ) # # state == normal and # MajorKey1!MinorKey1 followed by # MajorKey1!MinorKey1 # yields: # nextstate == multsubentry #
  • MajorKey1 #
      #
    • MinorKey1 # Recipe, # (note no final
    • ,
    or
  • ) # # state == multiple and # MajorKey1 followed by # MajorKey2 # yields: # nextstate == normal # Recipe1 # # state == multiple and # MajorKey1 followed by # MajorKey1 # yields: # nextstate == multiple # Recipe1, # (note no final ) # # state == subentry and # MajorKey1!MinorKey1 followed by # MajorKey2 # or MajorKey2!MinorKey2 # yields: # nextstate == normal #
  • MinorKey1
  • #
# # # state == subentry and # MajorKey1!MinorKey1 followed by # MajorKey1!MinorKey2 # yields: # nextstate == subentry #
  • MinorKey1,
  • # (note no final or ) # # state == subentry and # MajorKey1!MinorKey1 followed by # MajorKey1!MinorKey1 # yields: # nextstate == multsubentry #
  • MinorKey1 # Recipe, # (note no final
  • , or ) # # state == multsubentry and # MajorKey1!MinorKey1 followed by # MajorKey2 # or MajorKey2!MinorKey2 # yields: # nextstate == normal # Recipe # # # # state == multsubentry and # MajorKey1!MinorKey1 followed by # MajorKey1!MinorKey2 # yields: # nextstate == subentry # Recipe # (note no final or ) # # state == multsubentry and # MajorKey1!MinorKey1 followed by # MajorKey1!MinorKey1 # yields: # nextstate == multsubentry # Recipe, # (note no final , or ) # # state == multiple and # MajorKey1!MinorKey1 # is an illegal state # # state == subentry and # MajorKey1 # is an illegal state # # state == multsubentry and # MajorKey1 # is an illegal state # my $state = 'normal'; my $nextstate = 'normal'; for $num ( 0 .. $#sortedindex ) { $mainentry = $sortedindex[$num]{name}; if( $mainentry =~ m/(.+)!(.+)/ ) { $mainentry = $1; $minorentry = $2; } else { $minorentry = ''; } if( $num == $#sortedindex ) { # There is no next index entry, this is the last. if( $state eq 'normal' ) { print NEW qq(
  • $mainentry $sortedindex[$num]{recipe}
  • ); } elsif( $state eq 'multiple' ) { print NEW qq( $sortedindex[$num]{recipe} ); } elsif( $state eq 'subentry' ) { print NEW qq(
  • $sortedindex[$num]{recipe}
  • ); } elsif( $state eq 'multsubentry' ) { print NEW qq( $sortedindex[$num]{recipe} ); } } else { # There is a "next" entry in the sorted list # This is usually the case $nextmainentry = $sortedindex[$num + 1]{name}; if( $nextmainentry =~ m/(.+)!(.+)/ ) { $nextmainentry = $1; $nextminorentry = $2; } else { $nextminorentry = ''; } if( $state eq 'normal' ) { if( $mainentry ne $nextmainentry ) { print NEW qq(
  • $mainentry $sortedindex[$num]{recipe}
  • ); # $nextstate = 'normal'; implied } elsif( $minorentry eq '' ) { # Then this entry and the following are the same print NEW qq(
  • $mainentry $sortedindex[$num]{recipe}, ); # note no closing
  • $nextstate = 'multiple'; } else { # We need to see if the minor keys are equal or not if( $nextminorentry eq $minorentry ) { print NEW qq(
  • $mainentry or
  • $nextstate = 'multsubentry'; } else { print NEW qq(
  • $mainentry or
  • $nextstate = 'subentry'; } } } elsif( $state eq 'multiple' ) { if( $mainentry ne $nextmainentry ) { print NEW qq($sortedindex[$num]{recipe} ); # $nextstate = 'normal'; implied } else { # Then this entry and the following are the same print NEW qq($sortedindex[$num]{recipe}, ); # note no closing $nextstate = 'multiple'; } } elsif( $state eq 'subentry' ) { if( $mainentry ne $nextmainentry ) { print NEW qq(
  • $sortedindex[$num]{recipe}
  • ); # $nextstate = 'normal'; implied } else { # We need to see if the minor keys are equal or not if( $nextminorentry eq $minorentry ) { print NEW qq(
  • $minorentry $sortedindex[$num]{recipe}, ); # note no closing
  • , or $nextstate = 'multsubentry'; } else { print NEW qq(
  • $minorentry
  • ); # note no closing or $nextstate = 'subentry'; } } } elsif( $state eq 'multsubentry' ) { if( $mainentry ne $nextmainentry ) { print NEW qq($sortedindex[$num]{recipe} ); # $nextstate = 'normal'; implied } else { # We need to see if the minor keys are equal or not if( $nextminorentry eq $minorentry ) { print NEW qq( $sortedindex[$num]{recipe}, ); # note no closing , or $nextstate = 'multsubentry'; } else { print NEW qq( $sortedindex[$num]{recipe} ); # note no closing or $nextstate = 'subentry'; } } } } $state = $nextstate; $nextstate = 'normal'; } # Print the footer print NEW qq(
    wad\@alum.mit.edu
    );