#!/usr/bin/env perl # # make-index-html # John Simpson 2023-07-23 # # Create one or more 'index.html' files for use with web hosts that don't # automatically generate index pages (such as Keybase Sites) # # 2023-08-13 jms1 - add support for per-directory `.title` files # # 2023-11-11 jms1 - added "generated" timestamp at end of page # # 2023-12-19 jms1 - no functional change, just adding a note to remind myself # in the future ... tried to add timestamps when listing child directories, # you can't just use the directory's mtime since writing a new `index.html` # file within a directory updates the directory's mtime ... you'll need to # scan and remember the timestamps of every file *within* that directory, # and use the highest timestamp *within* a directory, and you'll need to # scan all files recursively *before* generating *any* `index.html` files, # and this is more than i feel like doing right now. # # 2025-01-03 jms1 # - made the green "alternating lines" colour more faded (#c0f0c0 -> #e0f0e0) # - added to generated pages # # 2025-01-18 jms1 # - changing logic around options, `-T` is no longer needed # - removed '-w' from shebang line, since some 'env' executables don't work # correctly if it's there. The 'use warnings' line below does the same thing. require 5.005 ; use strict ; use warnings ; use Getopt::Std ; ######################################## # Globals my @dirs = () ; # list of directories waiting to be processed my $root = '' ; # original root directory ######################################## # getopts my %opt = () ; my $sorted = 0 ; # -s generate "sorted" index pages my $recursive = 0 ; # -r process child directories my $overwrite = 0 ; # -f overwrite existing index pages my $set_title = '' ; # -t: manual title of root index page ############################################################################### # # usage sub usage(;$) { my $msg = ( shift || '' ) ; print < my $t_title = $p_title ; $t_title =~ s|<.*?>||gs ; ######################################## # Unless we've been told to overwrite files, make sure this file # doesn't already exist unless( $overwrite ) { if ( -f $outfile ) { print " EXISTS $outfile\n" ; return ; } } ######################################## # Create the file print " writing $outfile\n" ; open( my $fh , '>' , $outfile ) or die "ERROR: can't write \"$outfile\": $!\n" ; ######################################## # Write the header my $t = time() ; my @d = gmtime( $t ) ; my $now = sprintf( '%04d-%02d-%02dT%02d:%02d:%02d+00:00' , $d[5]+1900 , $d[4]+1 , $d[3] , $d[2] , $d[1] , $d[0] ) ; print $fh < $t_title

$p_title

$header
EOF ######################################## # Start the table print $fh < EOF ######################################## # Build possible links within header row my $nl = '' ; my $sl = '' ; my $dl = '' ; my $el = '' ; if ( $sorted ) { $nl = "" ; $sl = "" ; $dl = "" ; $el = '' ; } ######################################## # Write table header row print $fh < $nlName$el $slSize$el $dlDate$el EOF ######################################## # Write rows for child directories if ( $show_parent ) { print $fh < ../ EOF } for my $n ( sort keys %{$r_dirs} ) { print $fh < $n/ EOF } ######################################## # Figure out what order to show the files my @show = sort keys %{$r_files} ; if ( $sort_by eq 'size' ) { @show = sort { $r_files->{$a}->{'size'} <=> $r_files->{$b}->{'size'} } keys %{$r_files} ; } elsif ( $sort_by eq 'date' ) { @show = sort { $r_files->{$a}->{'mtime'} <=> $r_files->{$b}->{'mtime'} } keys %{$r_files} ; } ######################################## # Write rows for files for my $n ( @show ) { my $size = add_commas( $r_files->{$n}->{'size'} ) ; my $mtime = $r_files->{$n}->{'mtime'} ; my @d = gmtime( $mtime ) ; my $date = sprintf( '%04d-%02d-%02d %02d:%02d:%02d UTC' , $d[5]+1900 , $d[4]+1 , $d[3] , $d[2] , $d[1] , $d[0] ) ; print $fh < $n $size $date EOF } ######################################## # Finish the page print $fh <

Generated $now


$footer EOF close $fh ; } ############################################################################### # # Read the contents of a directory and generate one or more index file(s). sub process_dir($) { my $dir = shift ; my $header = '' ; my $footer = '' ; ############################################################ # Read directory's contents into memory my %ff = () ; # filename => { (mtime|size) => value } my %dd = () ; # dirname => 1 opendir( D , $dir ) or die "ERROR: opendir(\"$dir\"): $!\n" ; while( my $f = readdir( D ) ) { ######################################## # Certain filenames need to be ignored next if ( $f =~ m|^\.| ) ; next if ( $f eq 'index.html' ) ; next if ( $f eq 'index-s.html' ) ; next if ( $f eq 'index-d.html' ) ; ######################################## # If a HEADER.html or FOOTER.html file exsits, remember its contents # and don't show the file in the output. if ( $f eq 'HEADER.html' ) { print " reading HEADER.html\n" ; open( H , '<' , "$dir/$f" ) or die "ERROR: can't read \"$dir/$f\": $!\n" ; undef $/ ; $header = ; close H ; next ; } if ( $f eq 'FOOTER.html' ) { print " reading FOOTER.html\n" ; open( H , '<' , "$dir/$f" ) or die "ERROR: can't read \"$dir/$f\": $!\n" ; undef $/ ; $footer = ; close H ; next ; } ######################################## # Read/remember info about this entry if ( -f "$dir/$f" ) { my @s = stat( "$dir/$f" ) ; $ff{$f}->{'size'} = $s[7] ; $ff{$f}->{'mtime'} = $s[9] ; } elsif ( -d "$dir/$f" ) { my @s = stat( "$dir/$f" ) ; $dd{$f}->{'mtime'} = $s[9] ; if ( $recursive ) { push( @dirs , "$dir/$f" ) ; } } } closedir D ; ############################################################ # Build the title for this index my $sub = $dir ; $sub =~ s|^$root|| ; my $p_title = '' ; if ( $set_title ne '' ) { $p_title = $set_title . $sub ; } elsif ( -f "$dir/.title" ) { $p_title = '' ; open( T , '<' , "$dir/.title" ) or die "ERROR: can't read \"$dir/.title\": $!\n" ; while ( my $line = ) { $p_title .= $line ; } close T ; $p_title =~ s|\s+$||s ; } else { $p_title = "Index of $root$sub" ; } ######################################## # Write 'index.html' my $sp = ( $dir ne $root ) ? 1 : 0 ; write_index( "$dir/index.html" , $p_title , \%dd , \%ff , $header , $footer , '' , $sp ) ; ######################################## # If we're doing "sorted by" versions, generate those pages as well if ( $sorted ) { write_index( "$dir/index-s.html" , $p_title , \%dd , \%ff , $header , $footer , 'size' , $sp ) ; write_index( "$dir/index-d.html" , $p_title , \%dd , \%ff , $header , $footer , 'date' , $sp ) ; } } ############################################################################### ############################################################################### ############################################################################### # # Process the command line getopts( 'hsrft:' , \%opt ) ; $opt{'h'} && usage() ; $sorted = ( $opt{'s'} ? 1 : 0 ) ; $recursive = ( $opt{'r'} ? 1 : 0 ) ; $overwrite = ( $opt{'f'} ? 1 : 0 ) ; $set_title = ( $opt{'t'} || '' ) ; $root = ( shift || '.' ) ; $root =~ s|/$|| ; push( @dirs , $root ) ; ######################################## # Process the list of directories. # # When the script starts, the list only contains the directory that was # specified on the command line. # # If $recursive is true, other directories *may* be added to the list as # they are found. while ( my $d = shift @dirs ) { print "processing '$d'\n" ; process_dir( $d ) ; }