Rewrote manfmt to use AnyEvent and moved it to ManUtils
This commit is contained in:
parent
fadb0a1110
commit
5f87ab9069
2 changed files with 73 additions and 57 deletions
|
|
@ -2,11 +2,82 @@ package ManUtils;
|
||||||
|
|
||||||
use strict;
|
use strict;
|
||||||
use warnings;
|
use warnings;
|
||||||
|
use AE;
|
||||||
|
use AnyEvent::Util;
|
||||||
|
use Encode 'decode_utf8', 'encode_utf8';
|
||||||
|
|
||||||
|
|
||||||
our $VERSION = '0.01';
|
our $VERSION = '0.01';
|
||||||
|
|
||||||
require XSLoader;
|
require XSLoader;
|
||||||
XSLoader::load('ManUtils', $VERSION);
|
XSLoader::load('ManUtils', $VERSION);
|
||||||
|
|
||||||
|
|
||||||
|
# Usage: $cv = fmt($input, \$output, \@errors)
|
||||||
|
# $cv = AnyEvent condition variable, fired when done.
|
||||||
|
# $input = UTF-8 encoded manual page source
|
||||||
|
# $output = variable that will hold the output when done
|
||||||
|
# @errors = list of warnings/errors while running groff
|
||||||
|
sub fmt {
|
||||||
|
my($input, $output, $errors) = @_;
|
||||||
|
my $cv = AE::cv;
|
||||||
|
$$output = '';
|
||||||
|
@$errors = ();
|
||||||
|
|
||||||
|
# tix comes with[1] a custom(?) macro package. But it looks okay even without
|
||||||
|
# loading that.
|
||||||
|
# [1] It actually doesn't, the tcllib package appears to have that file, but
|
||||||
|
# doesn't '.so' it.
|
||||||
|
$input =~ s/^\.so man.macros$//mg;
|
||||||
|
# Other .so's should be handled by html()
|
||||||
|
$input =~ s/^\.so (.+)$/\[\[\[MANNEDINCLUDE $1\]\]\]/mg;
|
||||||
|
|
||||||
|
# Disable hyphenation, since that screws up man page references. :-(
|
||||||
|
$input = ".hy 0\n.de hy\n..\n$input";
|
||||||
|
|
||||||
|
$input = encode_utf8($input);
|
||||||
|
|
||||||
|
# Call grog to figure out which preprocessors to use.
|
||||||
|
# $MANWIDTH works by using the following groff options: -rLL=100n -rLT=100n
|
||||||
|
my $grog = run_cmd [qw|grog -Tutf8 -P-c -DUTF-8 -|],
|
||||||
|
'<' => \$input,
|
||||||
|
'>' => \my $cmd,
|
||||||
|
'2>' => sub { $_[0] && push @$errors, "grog: $_[0]" };
|
||||||
|
|
||||||
|
$grog->cb(sub {
|
||||||
|
chomp($cmd);
|
||||||
|
if(!$cmd || $cmd =~ /\n/) {
|
||||||
|
push @$errors, !$cmd ? 'grog failed to produce output' : "Excessive grog output: $cmd";
|
||||||
|
$cv->send;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
my $groff = run_cmd [split / /, $cmd],
|
||||||
|
'<' => \$input,
|
||||||
|
'>' => \my $fmt,
|
||||||
|
'2>' => sub { $_[0] && push @$errors, "groff: $_[0]" };
|
||||||
|
|
||||||
|
$groff->cb(sub {
|
||||||
|
$$output = $fmt ? decode_utf8($fmt) : '';
|
||||||
|
$$output =~ s/[\t\s\r\n]+$//;
|
||||||
|
$cv->send;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
$cv;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
# Blocking version of fmt(). Returns the formatted man page, errors are
|
||||||
|
# forwarded to warn().
|
||||||
|
sub fmt_block {
|
||||||
|
my $c = shift;
|
||||||
|
my $cv = fmt $c, \my $out, \my @err;
|
||||||
|
$cv->recv;
|
||||||
|
warn $_ for @err;
|
||||||
|
$out;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
1;
|
1;
|
||||||
|
|
||||||
|
|
|
||||||
59
www/index.pl
59
www/index.pl
|
|
@ -325,62 +325,6 @@ sub browsepkg {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
# TODO: Store/cache the result of this of this function in the database.
|
|
||||||
sub manfmt {
|
|
||||||
my $c = shift;
|
|
||||||
|
|
||||||
# tix comes with[1] a custom(?) macro package. But it looks okay even without
|
|
||||||
# loading that.
|
|
||||||
# [1] It actually doesn't, the tcllib package appears to have that file, but
|
|
||||||
# doesn't '.so' it.
|
|
||||||
$c =~ s/^\.so man.macros$//mg;
|
|
||||||
# Other .so's should be handled by the web interface
|
|
||||||
$c =~ s/^\.so (.+)$/\[\[\[MANNEDINCLUDE $1\]\]\]/mg;
|
|
||||||
|
|
||||||
# Disable hyphenation, since that screws up man page references. :-(
|
|
||||||
$c = ".hy 0\n.de hy\n..\n$c";
|
|
||||||
|
|
||||||
# Call grog to figure out which preprocessors to use.
|
|
||||||
# $MANWIDTH works by using the following groff options: -rLL=100n -rLT=100n
|
|
||||||
my($out, $in);
|
|
||||||
my $pid = open2($out, $in, qw|grog -Tutf8 -P-c -DUTF-8 -|);
|
|
||||||
binmode $in, ':utf8';
|
|
||||||
print $in $c;
|
|
||||||
close($in);
|
|
||||||
chomp(my $grog = <$out>);
|
|
||||||
waitpid $pid, 0;
|
|
||||||
|
|
||||||
# Call groff
|
|
||||||
$pid = open2($out, $in, split / /, $grog);
|
|
||||||
$c = encode_utf8($c);
|
|
||||||
my $ret;
|
|
||||||
# Read/write the data in chunks to avoid a deadlock on large I/O
|
|
||||||
while($c) {
|
|
||||||
my @a = IO::Select::select(IO::Select->new($out), IO::Select->new($in), undef);
|
|
||||||
die "IO::Select failed: $!\n" if !@a;
|
|
||||||
if(@{$a[0]}) {
|
|
||||||
my $b;
|
|
||||||
my $r = sysread($out, $b, 4096);
|
|
||||||
die "sysread failed: $!\n" if $r < 0;
|
|
||||||
$ret .= $b if $r;
|
|
||||||
}
|
|
||||||
if(@{$a[1]}) {
|
|
||||||
my $w = syswrite($in, $c, 4096);
|
|
||||||
die "syswrite failed: $!\n" if $w <= 0;
|
|
||||||
$c = substr($c, $w);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
close($in);
|
|
||||||
local $/;
|
|
||||||
$ret .= <$out>; # Now I'm mixing sysread and buffered read. I don't suppose that is an issue in this case, though.
|
|
||||||
waitpid $pid, 0;
|
|
||||||
|
|
||||||
$ret = decode_utf8($ret);
|
|
||||||
$ret =~ s/[\t\s\r\n]+$//;
|
|
||||||
return $ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
sub manjslist {
|
sub manjslist {
|
||||||
my($self, $m) = @_;
|
my($self, $m) = @_;
|
||||||
|
|
||||||
|
|
@ -486,7 +430,8 @@ sub man {
|
||||||
|
|
||||||
div id => 'contents';
|
div id => 'contents';
|
||||||
my $c = $self->dbManContent($man->{hash});
|
my $c = $self->dbManContent($man->{hash});
|
||||||
pre; lit ManUtils::html(manfmt $c); end;
|
# TODO: Store/cache the result of fmt() in the database.
|
||||||
|
pre; lit ManUtils::html(ManUtils::fmt_block $c); end;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
div id => 'locations';
|
div id => 'locations';
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue