diff -r 868dae1581ff -r 1ae1a79094fa Apache/OTL.pm --- a/Apache/OTL.pm Fri Jul 24 07:39:57 2009 -0700 +++ b/Apache/OTL.pm Fri Jul 24 07:49:06 2009 -0700 @@ -1,50 +1,89 @@ -#!/usr/bin/perl +# +# VimOutliner (OTL) XHTML pretty printer for mod_perl2/apache2. +# +# Copyright (c) 2006, Mahlon E. Smith +# All rights reserved. +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of Mahlon E. Smith nor the names of his +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY +# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY +# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +# package Apache::OTL; use strict; +use warnings; use Apache2::Const qw/ DECLINED OK /; -use Time::HiRes qw/ gettimeofday /; +use Apache2::Request; +use Apache2::RequestRec; +use Apache2::RequestUtil; +use Apache2::RequestIO; +use Apache2::Log; +use Time::HiRes 'gettimeofday'; sub handler { - my $r = shift; - my $VERSION = '0.4'; - my $t0 = Time::HiRes::gettimeofday; + my $VERSION = '0.5'; + my $ID = '$Id$'; + my $r = shift; + my $t0 = Time::HiRes::gettimeofday; my ( $file, # the absolute file path $title, # the file's title $uri, # the file uri - %re, # a hash of pre compiled regular expressions $data, # file contents - %opt, # options from the otl file @blocks, # todo groupings $mtime, # last modification time of otl file - %get, # get arguments (sorting, etc) + $get, # get arguments (sorting, etc) + %opt, # options from otl file ); - return DECLINED unless $r->method() eq 'GET'; - ($file, $uri) = ($r->filename, $r->uri); + # sanity checks + return DECLINED unless $r->method eq 'GET'; + + ( $file, $uri ) = ( $r->filename, $r->uri ); return DECLINED unless -e $file; $mtime = localtime( (stat(_))[9] ); - %get = $r->args; + my $req = Apache2::Request->new($r); + $get = $req->param || {}; - %re = - ( - title => qr/(?:.+)?\/(.+).otl$/i, - percent => qr/(\[.\]) (\d+)%/, - todo => qr/(\[_\]) /, - done => qr/(\[X\]) /, - comment => qr/^(?:\t+)?:(.+)/, - time => qr/(\d{2}:\d{2}:\d{2})/, - date => qr/(\d{2,4}-\d{2}-\d{2})/, - subitem => qr/^\t(?!\t)/, - line_wo_tabs => qr/^(?:\t+)?(.+)/, - linetext => qr/^(?:\[.\] (?:\d+%)?)? (.+)/, + my %re = ( + title => qr/(?:.+)?\/(.+).otl$/i, + percent => qr/(\[.\]) (\d+)%/, + todo => qr/(\[_\]) /, + done => qr/(\[X\]) /, + comment => qr/^(?:\t+)?:(.+)/, + time => qr/(\d{2}:\d{2}:\d{2})/, + date => qr/(\d{2,4}-\d{2}-\d{2})/, + subitem => qr/^\t(?!\t)/, + remove_tabs => qr/^(?:\t+)?(.+)/, + linetext => qr/^(?:\[.\] (?:\d+%)?)? (.+)/, + + comma_sep => qr/(?:\s+)?\,(?:\s+)?/, + hideline => qr/(?:\t+)?\#/, ); - open OTL, "$file" - || ( $r->log_error("Unable to read $file: $!") && return DECLINED ); + # snag file + open OTL, $file + or ( $r->log_error("Unable to read $file: $!") && return DECLINED ); do { local $/ = undef; $data = ; # shlorp @@ -52,7 +91,7 @@ close OTL; # just spit out the plain otl if requested. - if ($get{show} eq 'source') { + if ( $get->{'show'} && $get->{show} eq 'source' ) { $r->content_type('text/plain'); $r->print( $data ); return OK; @@ -78,7 +117,7 @@ } # GET args override settings - $opt{$_} = $get{$_} foreach keys %get; + $opt{$_} = $get->{$_} foreach keys %$get; # set title (fallback to file uri) $title = @@ -86,29 +125,36 @@ ? $opt{title} : $1 if $uri =~ $re{title}; - $opt{style} ||= '/otl_style.css'; - + # start html output $r->content_type('text/html'); $r->print(< $title - EHTML - if ($opt{js}) { - $r->print( - ' ' x 8, - "\n", - ' ' x 4, "\n", - "\n", - ); + # optional styles + if ( $opt{style} ) { + foreach ( split /$re{'comma_sep'}/, $opt{style} ) { + my $media = $_ =~ /print/ ? 'print' : 'screen'; + print qq{\t\n}; + } + } + + # optional javascript + if ( $opt{js} ) { + $r->print( "\t\n" ) + foreach split /$re{'comma_sep'}/, $opt{js}; + $r->print( ' ' x 4, "\n" ); + $r->print( ' ' x 4, "\n" ); } else { $r->print(< @@ -116,16 +162,19 @@ EHTML } + # title, last modification times $r->print("
$opt{title}
\n") if $opt{title}; $r->print("
Last modified: $mtime
\n") if $opt{last_mod}; if ($opt{legend}) { $r->print(< -  Item completed
-  Item is incomplete
+Item completed
+Item is incomplete
EHTML } + + # sorter if ($opt{sort}) { my %sorts = ( alpha => 'alphabetical', @@ -144,66 +193,95 @@ $r->print("\n"); } - my $bc = 0; foreach my $block ( sort { sorter(\%opt, \%re) } @blocks ) { # separate outline items - my @items = split /\n/, $block; - $r->print("
\n") if $opt{divs}; - my $lc = 0; - - # get item counts - my ($subs, $comments, $subsubs); - if ($opt{counts}) { - foreach (@items) { - if (/$re{comment}/) { - $comments++; - } elsif (/$re{subitem}/) { - $subs++; - } + my @lines = grep { $_ !~ /$re{'hideline'}/ } split /\n/, $block; + my $data = []; + + # build structure and get item counts + my ( $subs, $comments, $subsubs ) = ( 0, 0, 0 ); + foreach ( @lines ) { + if (/$re{comment}/) { + $comments++; + } + elsif (/$re{subitem}/) { + $subs++; } - $subsubs = (scalar @items - 1) - $subs - $comments;; + + my $level = 0; + $level = $1 =~ tr/\t/\t/ if /^(\t+)/; + $level++; + + s#$re{remove_tabs}#$1# unless $opt{'debug'}; + push @$data, [ $level, $_ ]; } + $subsubs = ( scalar @lines - 1 ) - $subs - $comments; - # parse - foreach (@items) { - next if /^\#/; - my $level = tr/\t/\t/ || 0; - next unless /\w/; + # begin parsing structure + $r->print("
\n"); + $r->print("
    \n") unless $opt{'debug'}; + my $i = 0; + foreach ( @$data ) { + my ( $level, $line ) = @$_; + + if ( $opt{'debug'} ) { + my $in = " " x $level x 4; + $r->print( "$level:$in $line
    \n" ); + next; + } + + my $next_level = $data->[ $i+1 ] ? $data->[ $i+1 ]->[0] : 0; + my $in = "\t" x $level; + + $line =~ s#$re{'time'}#$1#g; + $line =~ s#$re{date}#$1#g; + $line =~ s#$re{percent}#$1 $2%#; # append counts - if ($lc == 0 && $opt{counts} && $_ !~ $re{comment}) { + if ( $i == 0 && $opt{counts} && $line !~ $re{comment} ) { my $itmstr = $subs == 1 ? 'item' : 'items'; my $sitmstr = $subsubs == 1 ? 'subitem' : 'subitems'; - $_ .= " $subs $itmstr, $subsubs $sitmstr"; + $line .= " $subs $itmstr, $subsubs $sitmstr"; } - s/^:// if ! $level; - if ($opt{js}) { - s#(.+)#$1# if $lc == 0; - $r->print("\n") if $lc == 1; + my $li_class = '>'; + $li_class = ' class="todo">' if $line =~ s#$re{todo}##; + $li_class = ' class="done">' if $line =~ s#$re{done}##; + $li_class = ' class="comment">' if $line =~ s#$re{comment}#$1#; + + if ( $next_level == $level || $next_level == 0 ) { + $r->print( "$in\n" ); } - s#$re{'time'}#$1#g if /$re{'time'}/; - s#$re{date}#$1#g if /$re{date}/; - s#$re{percent}#$1 $2%# if /$re{percent}/; - s#$re{todo}# # if /$re{todo}/; - s#$re{done}# # if /$re{done}/; - s#$re{comment}#$1# if /$re{comment}/; - s#$re{line_wo_tabs}#$1#; + elsif ( $next_level < $level ) { + $r->print( "$in\n" ); + for (my $x = $level - 1; $x >= $next_level; $x--) { + my $in = "\t" x $x; + $r->print( "$in
\n$in\n" ); + } + } - $r->print("$_\n"); - $lc++; + else { + # implicit: $next_level > $level AND $next_level != 0 + $r->print("$in\n"); + } + + $i++; } - $r->print("\n") if $opt{js}; - $r->print("
\n") if $opt{divs}; - $r->print("


\n") if $opt{dividers}; - $r->print("

\n") unless $opt{divs} || $opt{dividers}; - $bc++; + + unless ( $opt{'debug'} ) { + for (my $x = $data->[ scalar @$data - 1]->[0] - 1; $x > 0; $x--) { + my $in = "\t" x $x; + $r->print( "$in\n$in\n" ); + } + $r->print( "\n" ); + } + $r->print( "
\n\n" ); } my $t1 = Time::HiRes::gettimeofday; my $td = sprintf("%0.3f", $t1 - $t0); - $r->print("
OTL parsed in $td secs
") if $opt{timer}; + $r->print("
OTL parsed in $td secs
\n") if $opt{timer}; $r->print(< @@ -230,3 +308,4 @@ } 1; +