#!/usr/bin/perl -w # Blosxom # Author: Rael Dornfest # Version: 0+3i # Home/Docs/Licensing: http://www.oreillynet.com/~rael/lang/perl/blosxom/ # --- Configurable variables ----- # What's my blog's title? my $blog_title = 'Blosxom'; # Where are my blog entries kept? my $datadir = "./entries"; # How many entries should I show on the home page? my $num_entries = 20; # HTTP Charrset attribute my $charset = "euc-jp"; # -------------------------------- use strict; use DirHandle; use FileHandle; use File::stat; use Time::localtime; use CGI qw/:standard :netscape/; # Take a gander at HTTP's PATH_INFO for optional blog name, archive yr/mo/day my $url = url(); my @pi = split m{/}, path_info(); shift @pi; my $content_type = 'text/' . ($pi[-1] && $pi[-1] =~ /xml/ ? pop @pi : 'html'); my $pi_bl = $pi[0] && $pi[0] =~ /^([a-zA-Z]\w*)/ ? shift @pi : undef; # Check the existence and readability of a specified sub-blog $pi_bl and -d "$datadir/$pi_bl" and -r "$datadir/$pi_bl" and $datadir .= "/$pi_bl", $url .= "/$pi_bl", $blog_title .= ": $pi_bl"; my($pi_yr,$pi_mo,$pi_da,$id) = @pi; my $dh = new DirHandle($datadir); my $fh = new FileHandle; # Bring in the templates my %template = (); use vars qw($default); for (split /\n/, $default) { last if /^(__END__)?$/; my($ct, $comp, $txt) = /^(\S+)\s(\S+)\s(.*)$/; $txt =~ s/\\n/\n/mg; $template{$ct}{$comp} = $txt; } # Header print header(-type => $content_type, -charset => $charset); my $head = join '', $content_type eq 'text/html' && $fh->open("< $datadir/head.html") ? <$fh> : $template{$content_type}{'head'}; $head =~ s/(\$\w+)/$1/gee; print $head; # Send in the blogs my $curdate = ''; foreach ( sort { return -M "$datadir/$a" <=> -M "$datadir/$b"; } grep /.txt$/, $dh->read ) { last if $num_entries-- <= 0 && !$pi_yr; my($fn) = ($_ =~ /^(.*)\.txt$/); # Date fiddling for by-{year,month,day} archive views my $mtime = ctime(stat("$datadir/$fn.txt")->mtime); my($dw,$mo,$da,$ti,$yr) = ( $mtime =~ /(\w{3}) +(\w{3}) +(\d{1,2}) +(\d{2}:\d{2}):\d{2} +(\d{4})$/ ); next if $pi_yr && $yr != $pi_yr; last if $pi_yr && $yr < $pi_yr; next if $pi_mo && $mo ne ucfirst(lc $pi_mo); next if $pi_da && $da != $pi_da; last if $pi_da && $da < $pi_da; $content_type eq 'text/html' && $curdate ne "$dw, $da $mo $yr" && print span({-class=>'blosxomDate'}, $curdate = "$dw, $da $mo $yr"); # Entry if (-f "$datadir/$fn.txt" && $fh->open("< $datadir/$fn.txt")) { chomp(my $title = <$fh>); chomp(my $body = join '', <$fh>); if ($content_type eq 'text/xml') { # Escape <, >, and &, and to produce valid RSS my %escape = ('<'=>'<', '>'=>'>', '&'=>'&', '"'=>'"'); my $escape_re = join '|' => keys %escape; $title =~ s/($escape_re)/$escape{$1}/g; $body =~ s/($escape_re)/$escape{$1}/g; } $fh->close; my $story = ($content_type eq 'text/html' && $fh->open("< $datadir/story.html")) ? join '', <$fh> : $template{$content_type}{'story'}; $story =~ s/(\$\w+)/$1/gee; print $story; $fh->close; } } # Foot print $content_type eq 'text/html' && $fh->open("< $datadir/foot.html") ? <$fh> : $template{$content_type}{'foot'}; # Default HTML and RSS template bits BEGIN { $default ||= <<'EOF'; text/html head Blosxom
$blog_title

text/html story

$title
$body
Posted at $ti #

\n text/html foot

Powered -- for some narrow definition of powered -- by Blosxom
text/xml head \n\n\n\n\n \n $blog_title$url\n text/xml story \n $title\n $url/$yr/$mo/$da#$fn\n $body\n \n text/xml foot \n EOF }