|
Packit |
e4b6da |
# vim:sw=4 sta et showmatch
|
|
Packit |
e4b6da |
|
|
Packit |
e4b6da |
# db2x_manxml - convert Man-XML to Texinfo
|
|
Packit |
e4b6da |
# (See docbook2X documentation for details)
|
|
Packit |
e4b6da |
#
|
|
Packit |
e4b6da |
# (C) 2000-2004 Steve Cheng <stevecheng@users.sourceforge.net>
|
|
Packit |
e4b6da |
#
|
|
Packit |
e4b6da |
# See the COPYING file in the docbook2X distribution
|
|
Packit |
e4b6da |
# for the copyright status of this software.
|
|
Packit |
e4b6da |
#
|
|
Packit |
e4b6da |
# Note: db2x_manxml.pl does not run by itself!
|
|
Packit |
e4b6da |
# It must be configured by including a config.pl file
|
|
Packit |
e4b6da |
# which is done when building docbook2X.
|
|
Packit |
e4b6da |
# In addition, the non-standard module
|
|
Packit |
e4b6da |
# XML::Handler::SGMLSpl must be explicitly loaded
|
|
Packit |
e4b6da |
# when docbook2X is not installed.
|
|
Packit |
e4b6da |
|
|
Packit |
e4b6da |
package main;
|
|
Packit |
e4b6da |
use strict;
|
|
Packit |
e4b6da |
|
|
Packit |
e4b6da |
|
|
Packit |
e4b6da |
##################################################
|
|
Packit |
e4b6da |
#
|
|
Packit |
e4b6da |
# Option parsing
|
|
Packit |
e4b6da |
#
|
|
Packit |
e4b6da |
##################################################
|
|
Packit |
e4b6da |
|
|
Packit |
e4b6da |
use Getopt::Long;
|
|
Packit |
e4b6da |
Getopt::Long::Configure('gnu_getopt');
|
|
Packit |
e4b6da |
my $cmdoptions = {
|
|
Packit |
e4b6da |
'encoding' => 'us-ascii',
|
|
Packit |
e4b6da |
'list-files' => 0,
|
|
Packit |
e4b6da |
'to-stdout' => 0,
|
|
Packit |
e4b6da |
'output-dir' => '',
|
|
Packit |
e4b6da |
'symlinks' => 0,
|
|
Packit |
e4b6da |
'solinks' => 0,
|
|
Packit |
e4b6da |
'no-links' => 0,
|
|
Packit |
e4b6da |
'utf8trans-program' => $db2x_config{'utf8trans-program'},
|
|
Packit |
e4b6da |
'utf8trans-map' => $db2x_config{'utf8trans-map-roff'},
|
|
Packit |
e4b6da |
'iconv-program' => $db2x_config{'iconv-program'},
|
|
Packit |
e4b6da |
};
|
|
Packit |
e4b6da |
|
|
Packit |
e4b6da |
sub options_help {
|
|
Packit |
e4b6da |
print "Usage: $0 [OPTION]... [FILE]...\n";
|
|
Packit |
e4b6da |
print <<'end';
|
|
Packit |
e4b6da |
Make man pages from Man-XML
|
|
Packit |
e4b6da |
|
|
Packit |
e4b6da |
--encoding=ENCODING Character encoding for man pages
|
|
Packit |
e4b6da |
Default is US-ASCII
|
|
Packit |
e4b6da |
--list-files Write list of output files to stdout
|
|
Packit |
e4b6da |
--to-stdout Write output to stdout instead of to files
|
|
Packit |
e4b6da |
--output-dir Directory to write the output files
|
|
Packit |
e4b6da |
Default is current working directory
|
|
Packit |
e4b6da |
|
|
Packit |
e4b6da |
Some man pages are made available under multiple names. Use one of
|
|
Packit |
e4b6da |
the following to select how should these names be made available:
|
|
Packit |
e4b6da |
--symlinks Symbolic links to principal man page
|
|
Packit |
e4b6da |
--solinks Stub pages with .so requests to principal man page
|
|
Packit |
e4b6da |
--no-links Make man page available only under principal name
|
|
Packit |
e4b6da |
|
|
Packit |
e4b6da |
|
|
Packit |
e4b6da |
These options set the location of auxiliary programs:
|
|
Packit |
e4b6da |
--utf8trans-program=PATH, --utf8trans-map=PATH, --iconv-program=PATH
|
|
Packit |
e4b6da |
|
|
Packit |
e4b6da |
--help Show this help and exit
|
|
Packit |
e4b6da |
--version Show version and exit
|
|
Packit |
e4b6da |
|
|
Packit |
e4b6da |
See the db2x_manxml(1) manual page and the docbook2X documentation for
|
|
Packit |
e4b6da |
more details.
|
|
Packit |
e4b6da |
end
|
|
Packit |
e4b6da |
exit 0;
|
|
Packit |
e4b6da |
}
|
|
Packit |
e4b6da |
|
|
Packit |
e4b6da |
sub options_version
|
|
Packit |
e4b6da |
{
|
|
Packit |
e4b6da |
print "db2x_manxml (part of docbook2X " .
|
|
Packit |
e4b6da |
$db2x_config{'docbook2X-version'} . ")\n";
|
|
Packit |
e4b6da |
print <<'end';
|
|
Packit |
e4b6da |
$Revision: 1.62 $ $Date: 2006/04/22 15:21:32 $
|
|
Packit |
e4b6da |
<URL:http://docbook2x.sourceforge.net/>
|
|
Packit |
e4b6da |
|
|
Packit |
e4b6da |
Copyright (C) 2000-2004 Steve Cheng
|
|
Packit |
e4b6da |
This is free software; see the source for copying conditions. There is NO
|
|
Packit |
e4b6da |
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
|
Packit |
e4b6da |
end
|
|
Packit |
e4b6da |
exit 0;
|
|
Packit |
e4b6da |
}
|
|
Packit |
e4b6da |
|
|
Packit |
e4b6da |
$SIG{__WARN__} = sub { print STDERR "$0: " . $_[0]; };
|
|
Packit |
e4b6da |
if(!GetOptions($cmdoptions,
|
|
Packit |
e4b6da |
'encoding=s',
|
|
Packit |
e4b6da |
'list-files',
|
|
Packit |
e4b6da |
'to-stdout',
|
|
Packit |
e4b6da |
'output-dir=s',
|
|
Packit |
e4b6da |
'symlinks|sym-links',
|
|
Packit |
e4b6da |
'solinks|so-links',
|
|
Packit |
e4b6da |
'no-links|nolinks',
|
|
Packit |
e4b6da |
'utf8trans-program=s',
|
|
Packit |
e4b6da |
'utf8trans-map=s',
|
|
Packit |
e4b6da |
'iconv-program=s',
|
|
Packit |
e4b6da |
'help', \&options_help,
|
|
Packit |
e4b6da |
'version', \&options_version))
|
|
Packit |
e4b6da |
{
|
|
Packit |
e4b6da |
print STDERR "Try \"$0 --help\" for more information.\n";
|
|
Packit |
e4b6da |
exit 1;
|
|
Packit |
e4b6da |
}
|
|
Packit |
e4b6da |
$SIG{__WARN__} = undef;
|
|
Packit |
e4b6da |
|
|
Packit |
e4b6da |
if($cmdoptions->{'symlinks'} +
|
|
Packit |
e4b6da |
$cmdoptions->{'solinks'} +
|
|
Packit |
e4b6da |
$cmdoptions->{'no-links'} > 1)
|
|
Packit |
e4b6da |
{
|
|
Packit |
e4b6da |
print STDERR "$0: Only one of --symlinks, --solinks or --no-links options is allowed!\n";
|
|
Packit |
e4b6da |
exit 1;
|
|
Packit |
e4b6da |
} elsif($cmdoptions->{'symlinks'} +
|
|
Packit |
e4b6da |
$cmdoptions->{'solinks'} +
|
|
Packit |
e4b6da |
$cmdoptions->{'no-links'} == 0)
|
|
Packit |
e4b6da |
{
|
|
Packit |
e4b6da |
$cmdoptions->{'no-links'} = 1;
|
|
Packit |
e4b6da |
}
|
|
Packit |
e4b6da |
|
|
Packit |
e4b6da |
#use XML::Handler::SGMLSpl; # we link to this explicitly during building
|
|
Packit |
e4b6da |
$manxml::templates = XML::Handler::SGMLSpl->new(
|
|
Packit |
e4b6da |
{ 'options' => $cmdoptions });
|
|
Packit |
e4b6da |
$manxml::templates->push_mode('file-unselected');
|
|
Packit |
e4b6da |
$manxml::templates->{namespaces}->{''} = "http://docbook2x.sourceforge.net/xmlns/Man-XML";
|
|
Packit |
e4b6da |
|
|
Packit |
e4b6da |
|
|
Packit |
e4b6da |
|
|
Packit |
e4b6da |
|
|
Packit |
e4b6da |
##################################################
|
|
Packit |
e4b6da |
#
|
|
Packit |
e4b6da |
# RoffWriter: Sanitized output routines
|
|
Packit |
e4b6da |
#
|
|
Packit |
e4b6da |
##################################################
|
|
Packit |
e4b6da |
|
|
Packit |
e4b6da |
package RoffWriter;
|
|
Packit |
e4b6da |
require Exporter;
|
|
Packit |
e4b6da |
@RoffWriter::ISA = qw(Exporter);
|
|
Packit |
e4b6da |
@RoffWriter::EXPORT_OK = qw(man_escape man_normalize_ws);
|
|
Packit |
e4b6da |
|
|
Packit |
e4b6da |
#
|
|
Packit |
e4b6da |
# Use RoffWriter on specified file
|
|
Packit |
e4b6da |
# Params: fh - an IO::Handle to send the output
|
|
Packit |
e4b6da |
#
|
|
Packit |
e4b6da |
sub new
|
|
Packit |
e4b6da |
{
|
|
Packit |
e4b6da |
my ($class, $fh) = @_;
|
|
Packit |
e4b6da |
my $self = { fh => $fh, line_start => 1, whitespace_last => 0 };
|
|
Packit |
e4b6da |
return bless($self, $class);
|
|
Packit |
e4b6da |
}
|
|
Packit |
e4b6da |
|
|
Packit |
e4b6da |
#
|
|
Packit |
e4b6da |
# Write output to filehandle
|
|
Packit |
e4b6da |
# or the string buffer (see buffer_{on,off}).
|
|
Packit |
e4b6da |
#
|
|
Packit |
e4b6da |
sub write
|
|
Packit |
e4b6da |
{
|
|
Packit |
e4b6da |
my ($self, $text) = @_;
|
|
Packit |
e4b6da |
if(defined $self->{stringbuffer}) {
|
|
Packit |
e4b6da |
$self->{stringbuffer} .= $text;
|
|
Packit |
e4b6da |
} else {
|
|
Packit |
e4b6da |
$self->{fh}->print($text);
|
|
Packit |
e4b6da |
}
|
|
Packit |
e4b6da |
}
|
|
Packit |
e4b6da |
|
|
Packit |
e4b6da |
#
|
|
Packit |
e4b6da |
# buffer_on, buffer_off
|
|
Packit |
e4b6da |
#
|
|
Packit |
e4b6da |
# Hack to allow the user to buffer output temporarily
|
|
Packit |
e4b6da |
# to a string which can be back pasted in later with write().
|
|
Packit |
e4b6da |
#
|
|
Packit |
e4b6da |
# This is used, in particular, for tbl output:
|
|
Packit |
e4b6da |
# The column and span width information must precede
|
|
Packit |
e4b6da |
# the cell data for each row, but the cell data may be
|
|
Packit |
e4b6da |
# arbitrarily complex text mixed with any number of
|
|
Packit |
e4b6da |
# roff requests.
|
|
Packit |
e4b6da |
#
|
|
Packit |
e4b6da |
# Don't take this facility lightly :)
|
|
Packit |
e4b6da |
# It is low-level: it intentionally does not save or restore
|
|
Packit |
e4b6da |
# the line_start internal state --- you must do that yourself,
|
|
Packit |
e4b6da |
# or use this facility only at well-defined points
|
|
Packit |
e4b6da |
# (e.g. only at line_start = 1).
|
|
Packit |
e4b6da |
#
|
|
Packit |
e4b6da |
sub buffer_on
|
|
Packit |
e4b6da |
{
|
|
Packit |
e4b6da |
my ($self) = @_;
|
|
Packit |
e4b6da |
$self->{stringbuffer} = '';
|
|
Packit |
e4b6da |
}
|
|
Packit |
e4b6da |
|
|
Packit |
e4b6da |
sub buffer_off
|
|
Packit |
e4b6da |
{
|
|
Packit |
e4b6da |
my ($self) = @_;
|
|
Packit |
e4b6da |
my $stringbuffer = $self->{stringbuffer};
|
|
Packit |
e4b6da |
$self->{stringbuffer} = undef;
|
|
Packit |
e4b6da |
return $stringbuffer;
|
|
Packit |
e4b6da |
}
|
|
Packit |
e4b6da |
|
|
Packit |
e4b6da |
#
|
|
Packit |
e4b6da |
# Print text with whitespace folding
|
|
Packit |
e4b6da |
# Usually need to escape text first
|
|
Packit |
e4b6da |
# Params: text - string to print
|
|
Packit |
e4b6da |
#
|
|
Packit |
e4b6da |
sub print_ws
|
|
Packit |
e4b6da |
{
|
|
Packit |
e4b6da |
my ($self, $text) = @_;
|
|
Packit |
e4b6da |
|
|
Packit |
e4b6da |
my @lines = split(/(\n)/, $text);
|
|
Packit |
e4b6da |
|
|
Packit |
e4b6da |
foreach my $line (@lines) {
|
|
Packit |
e4b6da |
# Go to beginning of next line
|
|
Packit |
e4b6da |
# unless already at beginning of a line
|
|
Packit |
e4b6da |
if($line eq "\n") {
|
|
Packit |
e4b6da |
$self->write("\n")
|
|
Packit |
e4b6da |
unless $self->{line_start}++;
|
|
Packit |
e4b6da |
} else {
|
|
Packit |
e4b6da |
# No spaces at the beginning of a line.
|
|
Packit |
e4b6da |
#
|
|
Packit |
e4b6da |
# The second if condition ensures that whenever the
|
|
Packit |
e4b6da |
# 'logical' input contains a string of whitespace, but
|
|
Packit |
e4b6da |
# the 'logical' input is split into two separate print_ws
|
|
Packit |
e4b6da |
# in the middle of the string of whitespace, like this:
|
|
Packit |
e4b6da |
#
|
|
Packit |
e4b6da |
# print_ws('text ') ; print_ws(' more text')
|
|
Packit |
e4b6da |
#
|
|
Packit |
e4b6da |
# Then the output should be 'text more text'
|
|
Packit |
e4b6da |
# with one space in between the (first two) words.
|
|
Packit |
e4b6da |
#
|
|
Packit |
e4b6da |
# NB: The use of 'whitespace' here excludes the newline.
|
|
Packit |
e4b6da |
# NB: whitespace_last is only meaningful when
|
|
Packit |
e4b6da |
# line_start == 0. This interpretation
|
|
Packit |
e4b6da |
# allows us to not have to update
|
|
Packit |
e4b6da |
# the whitespace_last status as much.
|
|
Packit |
e4b6da |
#
|
|
Packit |
e4b6da |
$line =~ s/^[ \t]+// if $self->{line_start}
|
|
Packit |
e4b6da |
or $self->{whitespace_last};
|
|
Packit |
e4b6da |
|
|
Packit |
e4b6da |
# "." and "'" get misinterpreted as a request
|
|
Packit |
e4b6da |
# at beginning of lines, so use a no-width space
|
|
Packit |
e4b6da |
# to prevent that.
|
|
Packit |
e4b6da |
$line =~ s/^([.'])/\\\&$1/ if $self->{line_start};
|
|
Packit |
e4b6da |
|
|
Packit |
e4b6da |
# Fold whitespaces in the middle of the line
|
|
Packit |
e4b6da |
$line =~ tr/ \t/ /s;
|
|
Packit |
e4b6da |
|
|
Packit |
e4b6da |
if($line ne '') {
|
|
Packit |
e4b6da |
$self->write($line);
|
|
Packit |
e4b6da |
$self->{line_start} = 0;
|
|
Packit |
e4b6da |
$self->{whitespace_last} = ($line =~ /[ \t]$/);
|
|
Packit |
e4b6da |
}
|
|
Packit |
e4b6da |
}
|
|
Packit |
e4b6da |
}
|
|
Packit |
e4b6da |
}
|
|
Packit |
e4b6da |
|
|
Packit |
e4b6da |
#
|
|
Packit |
e4b6da |
# Print text without folding whitespace
|
|
Packit |
e4b6da |
# Usually need to escape text first
|
|
Packit |
e4b6da |
# Params: text - string to print
|
|
Packit |
e4b6da |
#
|
|
Packit |
e4b6da |
sub print
|
|
Packit |
e4b6da |
{
|
|
Packit |
e4b6da |
my ($self, $text) = @_;
|
|
Packit |
e4b6da |
|
|
Packit |
e4b6da |
my @lines = split(/(\n)/, $text);
|
|
Packit |
e4b6da |
|
|
Packit |
e4b6da |
foreach my $line (@lines) {
|
|
Packit |
e4b6da |
if($line eq "\n") {
|
|
Packit |
e4b6da |
$self->write("\n");
|
|
Packit |
e4b6da |
$self->{line_start} = 1;
|
|
Packit |
e4b6da |
} else {
|
|
Packit |
e4b6da |
# "." and "'" get misinterpreted as a request
|
|
Packit |
e4b6da |
# at beginning of lines, so use a no-width space
|
|
Packit |
e4b6da |
# to prevent that.
|
|
Packit |
e4b6da |
$line =~ s/^([.'])/\\\&$1/ if $self->{line_start};
|
|
Packit |
e4b6da |
|
|
Packit |
e4b6da |
$self->{line_start} = ($line eq '');
|
|
Packit |
e4b6da |
$self->{whitespace_last} = ($line =~ /[ \t]$/);
|
|
Packit |
e4b6da |
$self->write($line);
|
|
Packit |
e4b6da |
}
|
|
Packit |
e4b6da |
}
|
|
Packit |
e4b6da |
}
|
|
Packit |
e4b6da |
|
|
Packit |
e4b6da |
sub skip_line
|
|
Packit |
e4b6da |
{
|
|
Packit |
e4b6da |
my ($self) = @_;
|
|
Packit |
e4b6da |
$self->write(($self->{line_start}++? "\n" : "\n\n"));
|
|
Packit |
e4b6da |
}
|
|
Packit |
e4b6da |
|
|
Packit |
e4b6da |
#
|
|
Packit |
e4b6da |
# Make a roff "request"
|
|
Packit |
e4b6da |
# Params: name - request identifier. The beginning '.' is optional.
|
|
Packit |
e4b6da |
# args - request arguments, in raw form.
|
|
Packit |
e4b6da |
# If there are characters to be escaped,
|
|
Packit |
e4b6da |
# wrap the string in a call to string_escape.
|
|
Packit |
e4b6da |
# Does not support using a different request start character.
|
|
Packit |
e4b6da |
#
|
|
Packit |
e4b6da |
sub request
|
|
Packit |
e4b6da |
{
|
|
Packit |
e4b6da |
my ($self, $name, @args) = @_;
|
|
Packit |
e4b6da |
|
|
Packit |
e4b6da |
$name = ".$name" if $name !~ /^[\.']/;
|
|
Packit |
e4b6da |
|
|
Packit |
e4b6da |
$self->write
|
|
Packit |
e4b6da |
($self->{line_start} ? "$name" : "\n$name");
|
|
Packit |
e4b6da |
|
|
Packit |
e4b6da |
foreach my $arg (@args) {
|
|
Packit |
e4b6da |
$arg =~ tr/\n/ /;
|
|
Packit |
e4b6da |
|
|
Packit |
e4b6da |
# Suggestion from groff Info manual.
|
|
Packit |
e4b6da |
# Escape double quotes using \(dq
|
|
Packit |
e4b6da |
$arg =~ s/"/\\\(dq/g;
|
|
Packit |
e4b6da |
|
|
Packit |
e4b6da |
# Quote arguments if either
|
|
Packit |
e4b6da |
# 1. the argument contains spaces
|
|
Packit |
e4b6da |
# 2. the argument is empty, and it is not the sole
|
|
Packit |
e4b6da |
# argument to the request
|
|
Packit |
e4b6da |
$arg = '"' . $arg . '"' if ($arg =~ / /
|
|
Packit |
e4b6da |
or (@args>1 and $arg eq ''));
|
|
Packit |
e4b6da |
$self->write(' ' . $arg);
|
|
Packit |
e4b6da |
}
|
|
Packit |
e4b6da |
|
|
Packit |
e4b6da |
$self->write("\n");
|
|
Packit |
e4b6da |
$self->{line_start} = 1;
|
|
Packit |
e4b6da |
}
|
|
Packit |
e4b6da |
|
|
Packit |
e4b6da |
#
|
|
Packit |
e4b6da |
# Print a comment in the output.
|
|
Packit |
e4b6da |
# Params: comment - the comment text.
|
|
Packit |
e4b6da |
# May use any characters; they need not be escaped.
|
|
Packit |
e4b6da |
#
|
|
Packit |
e4b6da |
sub comment
|
|
Packit |
e4b6da |
{
|
|
Packit |
e4b6da |
my ($self, $comment) = @_;
|
|
Packit |
e4b6da |
$self->write("\n") unless $self->{line_start};
|
|
Packit |
e4b6da |
|
|
Packit |
e4b6da |
foreach my $line (split(/\n/, $comment)) {
|
|
Packit |
e4b6da |
$self->write('.\" ');
|
|
Packit |
e4b6da |
$self->write($line);
|
|
Packit |
e4b6da |
$self->write("\n");
|
|
Packit |
e4b6da |
}
|
|
Packit |
e4b6da |
|
|
Packit |
e4b6da |
$self->{line_start} = 1;
|
|
Packit |
e4b6da |
}
|
|
Packit |
e4b6da |
|
|
Packit |
e4b6da |
|
|
Packit |
e4b6da |
#
|
|
Packit |
e4b6da |
# Use a roff "escape" i.e. commands embedded in text starting with \
|
|
Packit |
e4b6da |
# Params: escape - the escape sequence, excluding \
|
|
Packit |
e4b6da |
# Does not support using a different escape character.
|
|
Packit |
e4b6da |
#
|
|
Packit |
e4b6da |
sub escape
|
|
Packit |
e4b6da |
{
|
|
Packit |
e4b6da |
my ($self, $escape) = @_;
|
|
Packit |
e4b6da |
$self->write("\\$escape");
|
|
Packit |
e4b6da |
}
|
|
Packit |
e4b6da |
|
|
Packit |
e4b6da |
#
|
|
Packit |
e4b6da |
# Escape characters special to roff so they are displayed literally
|
|
Packit |
e4b6da |
# Params: s - the string to escape
|
|
Packit |
e4b6da |
# Returns: new string, with offending characters escaped
|
|
Packit |
e4b6da |
#
|
|
Packit |
e4b6da |
sub man_escape
|
|
Packit |
e4b6da |
{
|
|
Packit |
e4b6da |
my $s = shift;
|
|
Packit |
e4b6da |
$s =~ s/\\/\\e/g;
|
|
Packit |
e4b6da |
return $s;
|
|
Packit |
e4b6da |
}
|
|
Packit |
e4b6da |
|
|
Packit |
e4b6da |
sub man_normalize_ws
|
|
Packit |
e4b6da |
{
|
|
Packit |
e4b6da |
my $s = shift;
|
|
Packit |
e4b6da |
$s =~ tr/[ \t\n]/ /s;
|
|
Packit |
e4b6da |
$s =~ s/(^ )|( $)//;
|
|
Packit |
e4b6da |
return $s;
|
|
Packit |
e4b6da |
}
|
|
Packit |
e4b6da |
|
|
Packit |
e4b6da |
|
|
Packit |
e4b6da |
|
|
Packit |
e4b6da |
|
|
Packit |
e4b6da |
|
|
Packit |
e4b6da |
package manxml;
|
|
Packit |
e4b6da |
import RoffWriter qw(man_escape man_normalize_ws);
|
|
Packit |
e4b6da |
|
|
Packit |
e4b6da |
use IO::File;
|
|
Packit |
e4b6da |
use vars qw($templates);
|
|
Packit |
e4b6da |
|
|
Packit |
e4b6da |
##################################################
|
|
Packit |
e4b6da |
#
|
|
Packit |
e4b6da |
# Man page management
|
|
Packit |
e4b6da |
#
|
|
Packit |
e4b6da |
##################################################
|
|
Packit |
e4b6da |
|
|
Packit |
e4b6da |
# Remove leading and trailing blanks.
|
|
Packit |
e4b6da |
sub strip_string
|
|
Packit |
e4b6da |
{
|
|
Packit |
e4b6da |
my $str = shift;
|
|
Packit |
e4b6da |
|
|
Packit |
e4b6da |
$str = $1 if ($str =~ m#^\s*(\S.*)#);
|
|
Packit |
e4b6da |
$str = $1 if ($str =~ m#^(.*\S)\s*$#);
|
|
Packit |
e4b6da |
|
|
Packit |
e4b6da |
return $str;
|
|
Packit |
e4b6da |
}
|
|
Packit |
e4b6da |
|
|
Packit |
e4b6da |
#
|
|
Packit |
e4b6da |
# Generate a good file name for a man page, given a title and section
|
|
Packit |
e4b6da |
#
|
|
Packit |
e4b6da |
# Params: title - man page title
|
|
Packit |
e4b6da |
# section - 1, 2, 3, 4, etc.
|
|
Packit |
e4b6da |
#
|
|
Packit |
e4b6da |
sub man_filename
|
|
Packit |
e4b6da |
{
|
|
Packit |
e4b6da |
my $title = strip_string(shift);
|
|
Packit |
e4b6da |
my $sect = strip_string(shift);
|
|
Packit |
e4b6da |
|
|
Packit |
e4b6da |
# Escape the path separator '/' which is not allowed
|
|
Packit |
e4b6da |
# in Unix file names. (NUL is not allowed either, but
|
|
Packit |
e4b6da |
# it can't occur in XML data anyway.)
|
|
Packit |
e4b6da |
# Also escape lone . or .., or blank file name,
|
|
Packit |
e4b6da |
# which are also not allowed.
|
|
Packit |
e4b6da |
#
|
|
Packit |
e4b6da |
# We deliberate do NOT escape any other characters
|
|
Packit |
e4b6da |
# (e.g. space) because the user who happens to want
|
|
Packit |
e4b6da |
# to use a space in his file names, for whatever reason,
|
|
Packit |
e4b6da |
# would be justifiably annoyed at us if we had hard-coded
|
|
Packit |
e4b6da |
# any unnecessary escaping here.
|
|
Packit |
e4b6da |
#
|
|
Packit |
e4b6da |
# On the other hand, for people who want escaping, they
|
|
Packit |
e4b6da |
# can either fix their XML sources or stylesheets, very easily.
|
|
Packit |
e4b6da |
|
|
Packit |
e4b6da |
$title =~ tr/\//_/;
|
|
Packit |
e4b6da |
$sect =~ tr/\//_/;
|
|
Packit |
e4b6da |
|
|
Packit |
e4b6da |
$title =~ s/^(\.{0,2})$/_$1/;
|
|
Packit |
e4b6da |
|
|
Packit |
e4b6da |
return "$title.$sect";
|
|
Packit |
e4b6da |
}
|
|
Packit |
e4b6da |
|
|
Packit |
e4b6da |
sub shell_quote
|
|
Packit |
e4b6da |
{
|
|
Packit |
e4b6da |
join(' ', map { my $u = $_;
|
|
Packit |
e4b6da |
$u =~ s#([\$`"\\\n])#\\$1#g;
|
|
Packit |
e4b6da |
'"' . $u . '"' } @_);
|
|
Packit |
e4b6da |
}
|
|
Packit |
e4b6da |
|
|
Packit |
e4b6da |
sub man_openfile
|
|
Packit |
e4b6da |
{
|
|
Packit |
e4b6da |
my ($self, $filename) = @_;
|
|
Packit |
e4b6da |
my $encoding = $self->{options}->{'encoding'};
|
|
Packit |
e4b6da |
my $dir = $self->{options}->{'output-dir'};
|
|
Packit |
e4b6da |
$dir =~ s/([^\/])$/$1\//; # terminate with slash
|
|
Packit |
e4b6da |
|
|
Packit |
e4b6da |
my $openstr = '';
|
|
Packit |
e4b6da |
|
|
Packit |
e4b6da |
if(($encoding !~ /^utf|ucs/i or $encoding =~ s/\/\/TRANSLIT$//i)
|
|
Packit |
e4b6da |
and $self->{options}->{'utf8trans-program'} ne '')
|
|
Packit |
e4b6da |
{
|
|
Packit |
e4b6da |
$openstr .= '| ' .
|
|
Packit |
e4b6da |
shell_quote($self->{options}->{'utf8trans-program'}) . ' -- ' .
|
|
Packit |
e4b6da |
shell_quote($self->{options}->{'utf8trans-map'}) . ' ';
|
|
Packit |
e4b6da |
}
|
|
Packit |
e4b6da |
|
|
Packit |
e4b6da |
if($encoding !~ /^utf-?8$/i
|
|
Packit |
e4b6da |
and $self->{options}->{'iconv-program'} ne '')
|
|
Packit |
e4b6da |
{
|
|
Packit |
e4b6da |
$openstr .= '| ' .
|
|
Packit |
e4b6da |
shell_quote($self->{options}->{'iconv-program'},
|
|
Packit |
e4b6da |
'-f', 'utf-8',
|
|
Packit |
e4b6da |
'-t', $encoding)
|
|
Packit |
e4b6da |
. ' ';
|
|
Packit |
e4b6da |
}
|
|
Packit |
e4b6da |
|
|
Packit |
e4b6da |
if($openstr eq '') {
|
|
Packit |
e4b6da |
if(!$self->{options}->{'to-stdout'}) {
|
|
Packit |
e4b6da |
$openstr = "${dir}$filename";
|
|
Packit |
e4b6da |
# Trick from Perl FAQ to open file with arbitrary characters
|
|
Packit |
e4b6da |
$openstr =~ s#^(\s)#./$1#;
|
|
Packit |
e4b6da |
$openstr = ">${openstr}\0";
|
|
Packit |
e4b6da |
print "${dir}$filename\n"
|
|
Packit |
e4b6da |
if $self->{options}->{'list-files'};
|
|
Packit |
e4b6da |
} else {
|
|
Packit |
e4b6da |
$openstr = '>-';
|
|
Packit |
e4b6da |
}
|
|
Packit |
e4b6da |
} else {
|
|
Packit |
e4b6da |
if(!$self->{options}->{'to-stdout'}) {
|
|
Packit |
e4b6da |
$openstr .= '> ' . shell_quote("${dir}$filename");
|
|
Packit |
e4b6da |
print "${dir}$filename\n"
|
|
Packit |
e4b6da |
if $self->{options}->{'list-files'};
|
|
Packit |
e4b6da |
}
|
|
Packit |
e4b6da |
}
|
|
Packit |
e4b6da |
|
|
Packit |
e4b6da |
my $iof = new IO::File($openstr)
|
|
Packit |
e4b6da |
or die "$0: error opening $openstr: $!\n";
|
|
Packit |
e4b6da |
|
|
Packit |
e4b6da |
# Set output encoding to UTF-8 on Perl >=5.8.0
|
|
Packit |
e4b6da |
# so it doesn't complain
|
|
Packit |
e4b6da |
binmode($iof, ":utf8") unless $] < 5.008;
|
|
Packit |
e4b6da |
|
|
Packit |
e4b6da |
return $iof;
|
|
Packit |
e4b6da |
}
|
|
Packit |
e4b6da |
|
|
Packit |
e4b6da |
$templates->add_rule('manpage<', 'file-unselected', sub {
|
|
Packit |
e4b6da |
my ($self, $elem, $templates) = @_;
|
|
Packit |
e4b6da |
|
|
Packit |
e4b6da |
my $filename = man_filename(
|
|
Packit |
e4b6da |
$elem->attr('title'),
|
|
Packit |
e4b6da |
$elem->attr('sect'));
|
|
Packit |
e4b6da |
|
|
Packit |
e4b6da |
$self->{fh} = man_openfile($self, $filename);
|
|
Packit |
e4b6da |
$self->{rw} = new RoffWriter($self->{fh});
|
|
Packit |
e4b6da |
|
|
Packit |
e4b6da |
$self->{'adjust-stack'} = [ 'b' ];
|
|
Packit |
e4b6da |
|
|
Packit |
e4b6da |
$self->{rw}->comment($elem->attr('preprocessors'))
|
|
Packit |
e4b6da |
if($elem->attr('preprocessors') ne '');
|
|
Packit |
e4b6da |
|
|
Packit |
e4b6da |
# I've dug through the Internet to see if there was any
|
|
Packit |
e4b6da |
# standard way to specify encoding with man pages.
|
|
Packit |
e4b6da |
# The following seems to be a reasonable proposal:
|
|
Packit |
e4b6da |
# <URL:http://mail.nl.linux.org/linux-utf8/2001-04/msg00168.html>
|
|
Packit |
e4b6da |
my $encoding = $self->{options}->{'encoding'};
|
|
Packit |
e4b6da |
$encoding =~ s#//TRANSLIT$##i;
|
|
Packit |
e4b6da |
$self->{rw}->comment("-*- coding: $encoding -*-");
|
|
Packit |
e4b6da |
|
|
Packit |
e4b6da |
# Define escapes for switching to and from monospace fonts (groff only)
|
|
Packit |
e4b6da |
$self->{rw}->request(qw{ .if \n(.g .ds T< \\\\FC});
|
|
Packit |
e4b6da |
$self->{rw}->request(qw{ .if \n(.g .ds T> \\\\F[\n[.fam]]});
|
|
Packit |
e4b6da |
|
|
Packit |
e4b6da |
# Provide the URL macro
|
|
Packit |
e4b6da |
$self->{rw}->request(qw{ .de URL});
|
|
Packit |
e4b6da |
$self->{rw}->print('\\\\$2 \(la\\\\$1\(ra\\\\$3');
|
|
Packit |
e4b6da |
$self->{rw}->request('..');
|
|
Packit |
e4b6da |
$self->{rw}->request(qw{ .if \n(.g .mso www.tmac});
|
|
Packit |
e4b6da |
|
|
Packit |
e4b6da |
$self->{rw}->request('TH',
|
|
Packit |
e4b6da |
# Nothing in the man macros say this has to be the same as
|
|
Packit |
e4b6da |
# the $file and $sect. While it is best to follow convention,
|
|
Packit |
e4b6da |
# some stylesheets may want to uppercase/lowercase the name,
|
|
Packit |
e4b6da |
# so it is best leave this to them.
|
|
Packit |
e4b6da |
man_normalize_ws(man_escape($elem->attr('h1'))),
|
|
Packit |
e4b6da |
man_normalize_ws(man_escape($elem->attr('h2'))),
|
|
Packit |
e4b6da |
man_normalize_ws(man_escape($elem->attr('h3'))),
|
|
Packit |
e4b6da |
man_normalize_ws(man_escape($elem->attr('h4'))),
|
|
Packit |
e4b6da |
man_normalize_ws(man_escape($elem->attr('h5'))));
|
|
Packit |
e4b6da |
|
|
Packit |
e4b6da |
$templates->pop_mode();
|
|
Packit |
e4b6da |
});
|
|
Packit |
e4b6da |
|
|
Packit |
e4b6da |
$templates->add_rule('manpage>', 'file-unselected', sub {
|
|
Packit |
e4b6da |
my ($self, $elem, $templates) = @_;
|
|
Packit |
e4b6da |
|
|
Packit |
e4b6da |
$self->{rw}->print_ws("\n"); # ensure file ends in eol
|
|
Packit |
e4b6da |
$self->{fh}->close
|
|
Packit |
e4b6da |
or die $! ? "$0: error closing file/pipe: $!\n"
|
|
Packit |
e4b6da |
: "$0: program in pipeline exited with an error\n";
|
|
Packit |
e4b6da |
$self->{fh} = undef;
|
|
Packit |
e4b6da |
$self->{rw} = undef;
|
|
Packit |
e4b6da |
|
|
Packit |
e4b6da |
my $mainfilename = man_filename($elem->attr('title'),
|
|
Packit |
e4b6da |
$elem->attr('sect'));
|
|
Packit |
e4b6da |
|
|
Packit |
e4b6da |
# Make the files for the alternate names for the man page
|
|
Packit |
e4b6da |
foreach my $refname (@{$elem->ext->{'refnames'}})
|
|
Packit |
e4b6da |
{
|
|
Packit |
e4b6da |
my $filename = man_filename($refname, $elem->attr('sect'));
|
|
Packit |
e4b6da |
|
|
Packit |
e4b6da |
if($filename eq $mainfilename || $self->{options}->{'no-links'}
|
|
Packit |
e4b6da |
|| $self->{options}->{'to-stdout'})
|
|
Packit |
e4b6da |
{
|
|
Packit |
e4b6da |
# Same as main man page, don't make link.
|
|
Packit |
e4b6da |
}
|
|
Packit |
e4b6da |
else {
|
|
Packit |
e4b6da |
print STDOUT "$filename\n"
|
|
Packit |
e4b6da |
if $self->{options}->{'list-files'};
|
|
Packit |
e4b6da |
|
|
Packit |
e4b6da |
if($self->{options}->{'symlinks'}) {
|
|
Packit |
e4b6da |
symlink($mainfilename, $filename);
|
|
Packit |
e4b6da |
|
|
Packit |
e4b6da |
} else {
|
|
Packit |
e4b6da |
my $sectnum = $1 if $mainfilename =~ /^.+\.(\d)/;
|
|
Packit |
e4b6da |
|
|
Packit |
e4b6da |
my $fh = new IO::File $filename, "w";
|
|
Packit |
e4b6da |
my $rw = new RoffWriter($fh);
|
|
Packit |
e4b6da |
|
|
Packit |
e4b6da |
$rw->request('so',
|
|
Packit |
e4b6da |
man_escape("man${sectnum}/$mainfilename"));
|
|
Packit |
e4b6da |
|
|
Packit |
e4b6da |
$fh->close;
|
|
Packit |
e4b6da |
}
|
|
Packit |
e4b6da |
}
|
|
Packit |
e4b6da |
}
|
|
Packit |
e4b6da |
|
|
Packit |
e4b6da |
$templates->push_mode('file-unselected');
|
|
Packit |
e4b6da |
});
|
|
Packit |
e4b6da |
|
|
Packit |
e4b6da |
$templates->add_rule('manpageset<', 'file-unselected', sub {});
|
|
Packit |
e4b6da |
$templates->add_rule('manpageset>', 'file-unselected', sub {});
|
|
Packit |
e4b6da |
|
|
Packit |
e4b6da |
$templates->add_rule('text()', 'file-unselected', \&illegal_text_handler);
|
|
Packit |
e4b6da |
$templates->add_rule('*<', 'file-unselected', \&illegal_element_handler);
|
|
Packit |
e4b6da |
|
|
Packit |
e4b6da |
|
|
Packit |
e4b6da |
|
|
Packit |
e4b6da |
##################################################
|
|
Packit |
e4b6da |
#
|
|
Packit |
e4b6da |
# A clean solution to the extra-newlines problem
|
|
Packit |
e4b6da |
#
|
|
Packit |
e4b6da |
##################################################
|
|
Packit |
e4b6da |
|
|
Packit |
e4b6da |
# Call before almost block-level element; needed to ensure
|
|
Packit |
e4b6da |
# hanging indent and line-breaking before and after
|
|
Packit |
e4b6da |
# is handled correctly.
|
|
Packit |
e4b6da |
sub block_start
|
|
Packit |
e4b6da |
{
|
|
Packit |
e4b6da |
my ($self, $elem) = @_;
|
|
Packit |
e4b6da |
my $lastchild = $elem->parent->ext->{lastchild};
|
|
Packit |
e4b6da |
|
|
Packit |
e4b6da |
|
|
Packit |
e4b6da |
# There's no macro to stop the hang indent without
|
|
Packit |
e4b6da |
# starting a new paragraph (.PP), so it has to be
|
|
Packit |
e4b6da |
# done at the beginning of the next block.
|
|
Packit |
e4b6da |
if($lastchild eq 'hanging') {
|
|
Packit |
e4b6da |
$self->{rw}->request('PP');
|
|
Packit |
e4b6da |
}
|
|
Packit |
e4b6da |
|
|
Packit |
e4b6da |
# Conversely, here we are supposed to be hanging,
|
|
Packit |
e4b6da |
# so do not use .PP, which would nullify it
|
|
Packit |
e4b6da |
elsif($elem->within('TPitem')) {
|
|
Packit |
e4b6da |
$self->{rw}->skip_line()
|
|
Packit |
e4b6da |
unless $lastchild eq ''; # Don't put blank before first block
|
|
Packit |
e4b6da |
}
|
|
Packit |
e4b6da |
|
|
Packit |
e4b6da |
# Start a new 'paragraph'
|
|
Packit |
e4b6da |
elsif($lastchild eq 'block') {
|
|
Packit |
e4b6da |
# .PP macro messes up the indentation when
|
|
Packit |
e4b6da |
# used inside a table entry.
|
|
Packit |
e4b6da |
if($elem->parent->name eq 'entry') {
|
|
Packit |
e4b6da |
$self->{rw}->skip_line();
|
|
Packit |
e4b6da |
} else {
|
|
Packit |
e4b6da |
$self->{rw}->request('PP');
|
|
Packit |
e4b6da |
}
|
|
Packit |
e4b6da |
}
|
|
Packit |
e4b6da |
|
|
Packit |
e4b6da |
# Could also be done with .PP.
|
|
Packit |
e4b6da |
elsif($lastchild eq 'inline') {
|
|
Packit |
e4b6da |
$self->{rw}->skip_line();
|
|
Packit |
e4b6da |
}
|
|
Packit |
e4b6da |
|
|
Packit |
e4b6da |
$elem->parent->ext->{lastchild} = 'block';
|
|
Packit |
e4b6da |
}
|
|
Packit |
e4b6da |
|
|
Packit |
e4b6da |
sub mixed_inline_start
|
|
Packit |
e4b6da |
{
|
|
Packit |
e4b6da |
my ($self, $node) = @_;
|
|
Packit |
e4b6da |
|
|
Packit |
e4b6da |
# If text is just whitespace, then breaking should not be done
|
|
Packit |
e4b6da |
# at this point. Otherwise the following input
|
|
Packit |
e4b6da |
#
|
|
Packit |
e4b6da |
# <para>xxx<TP>...</TP>
|
|
Packit |
e4b6da |
# </para><para>yyy</para>
|
|
Packit |
e4b6da |
#
|
|
Packit |
e4b6da |
# would lead to extra .PP requests:
|
|
Packit |
e4b6da |
#
|
|
Packit |
e4b6da |
# xxx
|
|
Packit |
e4b6da |
# .PP
|
|
Packit |
e4b6da |
# .PP
|
|
Packit |
e4b6da |
# yyy
|
|
Packit |
e4b6da |
#
|
|
Packit |
e4b6da |
# the first .PP being caused by the newline
|
|
Packit |
e4b6da |
# at the end of the </TP> tag that is otherwise meaningless.
|
|
Packit |
e4b6da |
#
|
|
Packit |
e4b6da |
# So quit now if the text node is whitespace.
|
|
Packit |
e4b6da |
# Note this means we also do not change the lastchild
|
|
Packit |
e4b6da |
# status; this is correct behavior, because we still
|
|
Packit |
e4b6da |
# want breaking to be done later if necessary.
|
|
Packit |
e4b6da |
return if $node->{Data} !~ /[^ \t\r\n]/;
|
|
Packit |
e4b6da |
|
|
Packit |
e4b6da |
if($node->parent->ext->{lastchild} eq 'block') {
|
|
Packit |
e4b6da |
$self->{rw}->skip_line();
|
|
Packit |
e4b6da |
}
|
|
Packit |
e4b6da |
elsif($node->parent->ext->{lastchild} eq 'hanging') {
|
|
Packit |
e4b6da |
# Skipping a line after a hanging paragraph
|
|
Packit |
e4b6da |
# doesn't work, because we want to stop
|
|
Packit |
e4b6da |
# the indent
|
|
Packit |
e4b6da |
$self->{rw}->request('PP');
|
|
Packit |
e4b6da |
}
|
|
Packit |
e4b6da |
|
|
Packit |
e4b6da |
$node->parent->ext->{lastchild} = 'inline';
|
|
Packit |
e4b6da |
}
|
|
Packit |
e4b6da |
|
|
Packit |
e4b6da |
|
|
Packit |
e4b6da |
|
|
Packit |
e4b6da |
|
|
Packit |
e4b6da |
##################################################
|
|
Packit |
e4b6da |
#
|
|
Packit |
e4b6da |
# Changing fonts
|
|
Packit |
e4b6da |
#
|
|
Packit |
e4b6da |
##################################################
|
|
Packit |
e4b6da |
|
|
Packit |
e4b6da |
$templates->add_rule('b<', \&bold_start_handler);
|
|
Packit |
e4b6da |
$templates->add_rule('b>', \&bold_end_handler);
|
|
Packit |
e4b6da |
$templates->add_rule('i<', \&italic_start_handler);
|
|
Packit |
e4b6da |
$templates->add_rule('i>', \&italic_end_handler);
|
|
Packit |
e4b6da |
|
|
Packit |
e4b6da |
$templates->add_rule('b<', 'single-line-mode', \&bold_start_handler);
|
|
Packit |
e4b6da |
$templates->add_rule('b>', 'single-line-mode', \&bold_end_handler);
|
|
Packit |
e4b6da |
$templates->add_rule('i<', 'single-line-mode', \&italic_start_handler);
|
|
Packit |
e4b6da |
$templates->add_rule('i>', 'single-line-mode', \&italic_end_handler);
|
|
Packit |
e4b6da |
|
|
Packit |
e4b6da |
$templates->add_rule('b<', 'table-mode', \&bold_start_handler);
|
|
Packit |
e4b6da |
$templates->add_rule('b>', 'table-mode', \&bold_end_handler);
|
|
Packit |
e4b6da |
$templates->add_rule('i<', 'table-mode', \&italic_start_handler);
|
|
Packit |
e4b6da |
$templates->add_rule('i>', 'table-mode', \&italic_end_handler);
|
|
Packit |
e4b6da |
|
|
Packit |
e4b6da |
$templates->add_rule('b<', 'verbatim-mode', \&bold_start_handler);
|
|
Packit |
e4b6da |
$templates->add_rule('b>', 'verbatim-mode', \&bold_end_handler);
|
|
Packit |
e4b6da |
$templates->add_rule('i<', 'verbatim-mode', \&italic_start_handler);
|
|
Packit |
e4b6da |
$templates->add_rule('i>', 'verbatim-mode', \&italic_end_handler);
|
|
Packit |
e4b6da |
|
|
Packit |
e4b6da |
|
|
Packit |
e4b6da |
sub bold_start_handler {
|
|
Packit |
e4b6da |
my ($self, $elem, $templates) = @_;
|
|
Packit |
e4b6da |
mixed_inline_start($self, $elem);
|
|
Packit |
e4b6da |
|
|
Packit |
e4b6da |
# If the last font is also bold, don't change anything.
|
|
Packit |
e4b6da |
# Basically this is to just get more readable man output.
|
|
Packit |
e4b6da |
$self->{rw}->escape('fB')
|
|
Packit |
e4b6da |
unless $elem->in('b');
|
|
Packit |
e4b6da |
}
|
|
Packit |
e4b6da |
sub bold_end_handler {
|
|
Packit |
e4b6da |
my ($self, $elem, $templates) = @_;
|
|
Packit |
e4b6da |
|
|
Packit |
e4b6da |
if($elem->in('i')) {
|
|
Packit |
e4b6da |
$self->{rw}->escape('fI');
|
|
Packit |
e4b6da |
}
|
|
Packit |
e4b6da |
elsif($elem->in('b')) { }
|
|
Packit |
e4b6da |
else {
|
|
Packit |
e4b6da |
$self->{rw}->escape('fR');
|
|
Packit |
e4b6da |
}
|
|
Packit |
e4b6da |
}
|
|
Packit |
e4b6da |
|
|
Packit |
e4b6da |
sub italic_start_handler {
|
|
Packit |
e4b6da |
my ($self, $elem, $templates) = @_;
|
|
Packit |
e4b6da |
mixed_inline_start($self, $elem);
|
|
Packit |
e4b6da |
|
|
Packit |
e4b6da |
# If the last font is also bold, don't change anything.
|
|
Packit |
e4b6da |
# Basically this is to just get more readable man output.
|
|
Packit |
e4b6da |
$self->{rw}->escape('fI')
|
|
Packit |
e4b6da |
unless $elem->in('i');
|
|
Packit |
e4b6da |
}
|
|
Packit |
e4b6da |
sub italic_end_handler {
|
|
Packit |
e4b6da |
my ($self, $elem, $templates) = @_;
|
|
Packit |
e4b6da |
|
|
Packit |
e4b6da |
if($elem->in('b')) {
|
|
Packit |
e4b6da |
$self->{rw}->escape('fB');
|
|
Packit |
e4b6da |
}
|
|
Packit |
e4b6da |
elsif($elem->in('i')) { }
|
|
Packit |
e4b6da |
else {
|
|
Packit |
e4b6da |
$self->{rw}->escape('fR');
|
|
Packit |
e4b6da |
}
|
|
Packit |
e4b6da |
}
|
|
Packit |
e4b6da |
|
|
Packit |
e4b6da |
# Fixed-width fonts
|
|
Packit |
e4b6da |
#
|
|
Packit |
e4b6da |
# We do not use the font "CW", because you then cannot apply bold
|
|
Packit |
e4b6da |
# and italic to it. Although groff comes with the fonts
|
|
Packit |
e4b6da |
# "CI" and "CB" (in Postscript), they are not defined
|
|
Packit |
e4b6da |
# for TTY output!
|
|
Packit |
e4b6da |
#
|
|
Packit |
e4b6da |
# Switching the font family (with \Fx) is a groff extension; to be
|
|
Packit |
e4b6da |
# compatible with other man-page processors we use a custom escape
|
|
Packit |
e4b6da |
# instead of outputing \Fx directly. See the handling of the manpage
|
|
Packit |
e4b6da |
# element for the definition of these escapes.
|
|
Packit |
e4b6da |
#
|
|
Packit |
e4b6da |
sub monospace_start_handler {
|
|
Packit |
e4b6da |
my ($self, $elem, $templates) = @_;
|
|
Packit |
e4b6da |
|
|
Packit |
e4b6da |
if(not $elem->within('tt')) {
|
|
Packit |
e4b6da |
$self->{rw}->escape('*(T<');
|
|
Packit |
e4b6da |
}
|
|
Packit |
e4b6da |
}
|
|
Packit |
e4b6da |
sub monospace_end_handler {
|
|
Packit |
e4b6da |
my ($self, $elem, $templates) = @_;
|
|
Packit |
e4b6da |
|
|
Packit |
e4b6da |
if(not $elem->within('tt')) {
|
|
Packit |
e4b6da |
$self->{rw}->escape('*(T>');
|
|
Packit |
e4b6da |
}
|
|
Packit |
e4b6da |
}
|
|
Packit |
e4b6da |
|
|
Packit |
e4b6da |
$templates->add_rule('tt<', \&monospace_start_handler);
|
|
Packit |
e4b6da |
$templates->add_rule('tt>', \&monospace_end_handler);
|
|
Packit |
e4b6da |
$templates->add_rule('tt<', 'single-line-mode', \&monospace_start_handler);
|
|
Packit |
e4b6da |
$templates->add_rule('tt>', 'single-line-mode', \&monospace_end_handler);
|
|
Packit |
e4b6da |
$templates->add_rule('tt<', 'table-mode', \&monospace_start_handler);
|
|
Packit |
e4b6da |
$templates->add_rule('tt>', 'table-mode', \&monospace_end_handler);
|
|
Packit |
e4b6da |
$templates->add_rule('tt<', 'verbatim-mode', \&monospace_start_handler);
|
|
Packit |
e4b6da |
$templates->add_rule('tt>', 'verbatim-mode', \&monospace_end_handler);
|
|
Packit |
e4b6da |
|
|
Packit |
e4b6da |
#
|
|
Packit |
e4b6da |
# Groff URL macros
|
|
Packit |
e4b6da |
#
|
|
Packit |
e4b6da |
sub ulink_start_handler {
|
|
Packit |
e4b6da |
my ($self, $elem, $templates) = @_;
|
|
Packit |
e4b6da |
|
|
Packit |
e4b6da |
$templates->push_mode('section-mode');
|
|
Packit |
e4b6da |
$self->{output_save} = '';
|
|
Packit |
e4b6da |
}
|
|
Packit |
e4b6da |
sub ulink_end_handler {
|
|
Packit |
e4b6da |
my ($self, $elem, $templates) = @_;
|
|
Packit |
e4b6da |
|
|
Packit |
e4b6da |
$templates->pop_mode();
|
|
Packit |
e4b6da |
my $s = $self->{output_save};
|
|
Packit |
e4b6da |
$self->{output_save} = undef;
|
|
Packit |
e4b6da |
|
|
Packit |
e4b6da |
$self->{rw}->request('URL',
|
|
Packit |
e4b6da |
man_escape($elem->attr('url')), $s);
|
|
Packit |
e4b6da |
}
|
|
Packit |
e4b6da |
sub ulink_start_plain_handler {
|
|
Packit |
e4b6da |
my ($self, $elem, $templates) = @_;
|
|
Packit |
e4b6da |
}
|
|
Packit |
e4b6da |
sub ulink_end_plain_handler {
|
|
Packit |
e4b6da |
my ($self, $elem, $templates) = @_;
|
|
Packit |
e4b6da |
$self->{rw}->print_ws(
|
|
Packit |
e4b6da |
' \(la' . man_escape($elem->attr('ulink')) . '\(ra ');
|
|
Packit |
e4b6da |
}
|
|
Packit |
e4b6da |
|
|
Packit |
e4b6da |
$templates->add_rule('ulink<', \&ulink_start_handler);
|
|
Packit |
e4b6da |
$templates->add_rule('ulink>', \&ulink_end_handler);
|
|
Packit |
e4b6da |
$templates->add_rule('ulink<', 'single-line-mode', \&ulink_start_plain_handler);
|
|
Packit |
e4b6da |
$templates->add_rule('ulink>', 'single-line-mode', \&ulink_end_plain_handler);
|
|
Packit |
e4b6da |
$templates->add_rule('ulink<', 'table-mode', \&ulink_start_handler);
|
|
Packit |
e4b6da |
$templates->add_rule('ulink>', 'table-mode', \&ulink_end_handler);
|
|
Packit |
e4b6da |
$templates->add_rule('ulink<', 'verbatim-mode', \&ulink_start_plain_handler);
|
|
Packit |
e4b6da |
$templates->add_rule('ulink>', 'verbatim-mode', \&ulink_end_plain_handler);
|
|
Packit |
e4b6da |
|
|
Packit |
e4b6da |
#
|
|
Packit |
e4b6da |
# Turn off/on hyphenation (used for long URI's and the like)
|
|
Packit |
e4b6da |
#
|
|
Packit |
e4b6da |
sub hyphenation_off {
|
|
Packit |
e4b6da |
my ($self, $elem, $templates) = @_;
|
|
Packit |
e4b6da |
return if $templates->mode() eq 'single-line-mode';
|
|
Packit |
e4b6da |
$self->{rw}->request("'nh");
|
|
Packit |
e4b6da |
}
|
|
Packit |
e4b6da |
sub hyphenation_on {
|
|
Packit |
e4b6da |
my ($self, $elem, $templates) = @_;
|
|
Packit |
e4b6da |
return if $templates->mode() eq 'single-line-mode';
|
|
Packit |
e4b6da |
$self->{rw}->request("'hy");
|
|
Packit |
e4b6da |
}
|
|
Packit |
e4b6da |
|
|
Packit |
e4b6da |
$templates->add_rule('nh<', \&hyphenation_off);
|
|
Packit |
e4b6da |
$templates->add_rule('nh>', \&hyphenation_on);
|
|
Packit |
e4b6da |
$templates->add_rule('nh<', 'table-mode', \&hyphenation_off);
|
|
Packit |
e4b6da |
$templates->add_rule('nh>', 'table-mode', \&hyphenation_on);
|
|
Packit |
e4b6da |
$templates->add_rule('nh<', 'single-line-mode', sub {});
|
|
Packit |
e4b6da |
$templates->add_rule('nh<', 'verbatim-mode', sub {});
|
|
Packit |
e4b6da |
|
|
Packit |
e4b6da |
# roff distinguishes between the ASCII hyphen-minus
|
|
Packit |
e4b6da |
# and a normal hyphen.
|
|
Packit |
e4b6da |
# See <URL:http://www.cs.tut.fi/~jkorpela/dashes.html>
|
|
Packit |
e4b6da |
|
|
Packit |
e4b6da |
sub disambiguate_hyphen_minus
|
|
Packit |
e4b6da |
{
|
|
Packit |
e4b6da |
my ($text, $elem) = @_;
|
|
Packit |
e4b6da |
|
|
Packit |
e4b6da |
if(!$elem or $elem->within('tt') or $elem->within('verbatim')) {
|
|
Packit |
e4b6da |
$text =~ s/-/\\-/g;
|
|
Packit |
e4b6da |
}
|
|
Packit |
e4b6da |
|
|
Packit |
e4b6da |
return $text;
|
|
Packit |
e4b6da |
}
|
|
Packit |
e4b6da |
|
|
Packit |
e4b6da |
sub disable_hyphenation
|
|
Packit |
e4b6da |
{
|
|
Packit |
e4b6da |
my ($text, $elem) = @_;
|
|
Packit |
e4b6da |
return $text; # Disable for now
|
|
Packit |
e4b6da |
|
|
Packit |
e4b6da |
if($elem and $elem->within('tt') and not $elem->within('verbatim')) {
|
|
Packit |
e4b6da |
$text =~ s/ / \\%/g;
|
|
Packit |
e4b6da |
}
|
|
Packit |
e4b6da |
return $text;
|
|
Packit |
e4b6da |
}
|
|
Packit |
e4b6da |
|
|
Packit |
e4b6da |
##################################################
|
|
Packit |
e4b6da |
#
|
|
Packit |
e4b6da |
# NAME section
|
|
Packit |
e4b6da |
#
|
|
Packit |
e4b6da |
##################################################
|
|
Packit |
e4b6da |
|
|
Packit |
e4b6da |
# The man page database parser needs the names and
|
|
Packit |
e4b6da |
# description all on one line, so enter a special mode to do that.
|
|
Packit |
e4b6da |
$templates->add_rule('refnameline<', sub {
|
|
Packit |
e4b6da |
my ($self, $elem, $templates) = @_;
|
|
Packit |
e4b6da |
$elem->parent->ext->{lastchild} = 'block';
|
|
Packit |
e4b6da |
$templates->push_mode('refnameline-mode');
|
|
Packit |
e4b6da |
});
|
|
Packit |
e4b6da |
|
|
Packit |
e4b6da |
$templates->add_rule('refnameline>', sub {
|
|
Packit |
e4b6da |
my ($self, $elem, $templates) = @_;
|
|
Packit |
e4b6da |
$templates->pop_mode();
|
|
Packit |
e4b6da |
$self->{rw}->print_ws("\n");
|
|
Packit |
e4b6da |
$elem->ext->{'refnames'} = 0;
|
|
Packit |
e4b6da |
});
|
|
Packit |
e4b6da |
|
|
Packit |
e4b6da |
$templates->add_rule('text()', 'refnameline-mode',
|
|
Packit |
e4b6da |
\&single_line_text_handler);
|
|
Packit |
e4b6da |
|
|
Packit |
e4b6da |
$templates->add_rule('refname<', 'refnameline-mode', sub {
|
|
Packit |
e4b6da |
my ($self, $elem, $templates) = @_;
|
|
Packit |
e4b6da |
$templates->push_mode('refname');
|
|
Packit |
e4b6da |
$self->{output_save} = '';
|
|
Packit |
e4b6da |
});
|
|
Packit |
e4b6da |
$templates->add_rule('refname>', 'refnameline-mode', sub {
|
|
Packit |
e4b6da |
my ($self, $elem, $templates) = @_;
|
|
Packit |
e4b6da |
$templates->pop_mode();
|
|
Packit |
e4b6da |
|
|
Packit |
e4b6da |
if($elem->parent->ext->{'refnames'}++) {
|
|
Packit |
e4b6da |
$self->{rw}->print_ws(', ');
|
|
Packit |
e4b6da |
}
|
|
Packit |
e4b6da |
|
|
Packit |
e4b6da |
$self->{rw}->print_ws(man_escape($self->{output_save}));
|
|
Packit |
e4b6da |
|
|
Packit |
e4b6da |
# Store the refname; needed later to make the links.
|
|
Packit |
e4b6da |
push(@{$elem->parent->parent->ext->{refnames}},
|
|
Packit |
e4b6da |
$self->{output_save});
|
|
Packit |
e4b6da |
$self->{output_save} = undef;
|
|
Packit |
e4b6da |
});
|
|
Packit |
e4b6da |
$templates->add_rule('text()', 'refname',
|
|
Packit |
e4b6da |
\&save_text_handler);
|
|
Packit |
e4b6da |
|
|
Packit |
e4b6da |
$templates->add_rule('refpurpose<', 'refnameline-mode', sub {
|
|
Packit |
e4b6da |
my ($self, $elem, $templates) = @_;
|
|
Packit |
e4b6da |
$self->{rw}->print_ws(' \- ');
|
|
Packit |
e4b6da |
});
|
|
Packit |
e4b6da |
|
|
Packit |
e4b6da |
|
|
Packit |
e4b6da |
|
|
Packit |
e4b6da |
|
|
Packit |
e4b6da |
|
|
Packit |
e4b6da |
|
|
Packit |
e4b6da |
##################################################
|
|
Packit |
e4b6da |
#
|
|
Packit |
e4b6da |
# Section headings
|
|
Packit |
e4b6da |
#
|
|
Packit |
e4b6da |
##################################################
|
|
Packit |
e4b6da |
|
|
Packit |
e4b6da |
$templates->add_rule("SH<", sub {
|
|
Packit |
e4b6da |
my ($self, $elem, $templates) = @_;
|
|
Packit |
e4b6da |
$elem->parent->ext->{lastchild} = '';
|
|
Packit |
e4b6da |
$templates->push_mode('section-mode');
|
|
Packit |
e4b6da |
$self->{output_save} = '';
|
|
Packit |
e4b6da |
});
|
|
Packit |
e4b6da |
|
|
Packit |
e4b6da |
$templates->add_rule("SH>", sub {
|
|
Packit |
e4b6da |
my ($self, $elem, $templates) = @_;
|
|
Packit |
e4b6da |
$templates->pop_mode();
|
|
Packit |
e4b6da |
$self->{rw}->request('SH',
|
|
Packit |
e4b6da |
man_normalize_ws(man_escape($self->{output_save})));
|
|
Packit |
e4b6da |
$self->{output_save} = undef;
|
|
Packit |
e4b6da |
});
|
|
Packit |
e4b6da |
|
|
Packit |
e4b6da |
$templates->add_rule("SS<", sub {
|
|
Packit |
e4b6da |
my ($self, $elem, $templates) = @_;
|
|
Packit |
e4b6da |
$elem->parent->ext->{lastchild} = '';
|
|
Packit |
e4b6da |
$templates->push_mode('section-mode');
|
|
Packit |
e4b6da |
$self->{output_save} = '';
|
|
Packit |
e4b6da |
});
|
|
Packit |
e4b6da |
|
|
Packit |
e4b6da |
$templates->add_rule("SS>", sub {
|
|
Packit |
e4b6da |
my ($self, $elem, $templates) = @_;
|
|
Packit |
e4b6da |
$templates->pop_mode();
|
|
Packit |
e4b6da |
$self->{rw}->request('SS',
|
|
Packit |
e4b6da |
man_normalize_ws(man_escape($self->{output_save})));
|
|
Packit |
e4b6da |
$self->{output_save} = undef;
|
|
Packit |
e4b6da |
});
|
|
Packit |
e4b6da |
|
|
Packit |
e4b6da |
$templates->add_rule('text()', 'section-mode',
|
|
Packit |
e4b6da |
\&save_text_handler);
|
|
Packit |
e4b6da |
|
|
Packit |
e4b6da |
$templates->add_rule('b<', 'section-mode', sub {});
|
|
Packit |
e4b6da |
$templates->add_rule('i<', 'section-mode', sub {});
|
|
Packit |
e4b6da |
$templates->add_rule('tt<', 'section-mode', sub {});
|
|
Packit |
e4b6da |
$templates->add_rule('*<', 'section-mode', \&illegal_element_handler);
|
|
Packit |
e4b6da |
|
|
Packit |
e4b6da |
|
|
Packit |
e4b6da |
|
|
Packit |
e4b6da |
##################################################
|
|
Packit |
e4b6da |
#
|
|
Packit |
e4b6da |
# Paragraph
|
|
Packit |
e4b6da |
#
|
|
Packit |
e4b6da |
##################################################
|
|
Packit |
e4b6da |
|
|
Packit |
e4b6da |
$templates->add_rule('para<', sub {
|
|
Packit |
e4b6da |
my ($self, $elem, $templates) = @_;
|
|
Packit |
e4b6da |
block_start($self, $elem);
|
|
Packit |
e4b6da |
|
|
Packit |
e4b6da |
my $adstack = $self->{'adjust-stack'};
|
|
Packit |
e4b6da |
if($elem->attr('align') ne '') {
|
|
Packit |
e4b6da |
$self->{rw}->request('fi');
|
|
Packit |
e4b6da |
$self->{rw}->request('ad', $elem->attr('align'));
|
|
Packit |
e4b6da |
push(@$adstack, $elem->attr('align'));
|
|
Packit |
e4b6da |
}
|
|
Packit |
e4b6da |
});
|
|
Packit |
e4b6da |
|
|
Packit |
e4b6da |
$templates->add_rule('para>', sub {
|
|
Packit |
e4b6da |
my ($self, $elem, $templates) = @_;
|
|
Packit |
e4b6da |
my $adstack = $self->{'adjust-stack'};
|
|
Packit |
e4b6da |
if($elem->attr('align') ne '') {
|
|
Packit |
e4b6da |
pop(@$adstack);
|
|
Packit |
e4b6da |
$self->{rw}->request('ad', $adstack->[-1]);
|
|
Packit |
e4b6da |
}
|
|
Packit |
e4b6da |
});
|
|
Packit |
e4b6da |
|
|
Packit |
e4b6da |
|
|
Packit |
e4b6da |
##################################################
|
|
Packit |
e4b6da |
#
|
|
Packit |
e4b6da |
# Indented paragraphs of various sorts
|
|
Packit |
e4b6da |
#
|
|
Packit |
e4b6da |
##################################################
|
|
Packit |
e4b6da |
|
|
Packit |
e4b6da |
$templates->add_rule('TP<', sub {
|
|
Packit |
e4b6da |
my ($self, $elem, $templates) = @_;
|
|
Packit |
e4b6da |
$elem->parent->ext->{lastchild} = 'hanging';
|
|
Packit |
e4b6da |
});
|
|
Packit |
e4b6da |
|
|
Packit |
e4b6da |
$templates->add_rule('TPauto<', sub {
|
|
Packit |
e4b6da |
my ($self, $elem, $templates) = @_;
|
|
Packit |
e4b6da |
block_start($self, $elem);
|
|
Packit |
e4b6da |
|
|
Packit |
e4b6da |
my $adstack = $self->{'adjust-stack'};
|
|
Packit |
e4b6da |
if($elem->attr('align') ne '') {
|
|
Packit |
e4b6da |
$self->{rw}->request('fi');
|
|
Packit |
e4b6da |
$self->{rw}->request('ad', $elem->attr('align'));
|
|
Packit |
e4b6da |
push(@$adstack, $elem->attr('align'));
|
|
Packit |
e4b6da |
}
|
|
Packit |
e4b6da |
});
|
|
Packit |
e4b6da |
|
|
Packit |
e4b6da |
$templates->add_rule('TPauto>', sub {
|
|
Packit |
e4b6da |
my ($self, $elem, $templates) = @_;
|
|
Packit |
e4b6da |
my $adstack = $self->{'adjust-stack'};
|
|
Packit |
e4b6da |
if($elem->attr('align') ne '') {
|
|
Packit |
e4b6da |
pop(@$adstack);
|
|
Packit |
e4b6da |
$self->{rw}->request('ad', $adstack->[-1]);
|
|
Packit |
e4b6da |
}
|
|
Packit |
e4b6da |
});
|
|
Packit |
e4b6da |
|
|
Packit |
e4b6da |
$templates->add_rule('TPtag<', sub {
|
|
Packit |
e4b6da |
my ($self, $elem, $templates) = @_;
|
|
Packit |
e4b6da |
|
|
Packit |
e4b6da |
if($elem->in('TP')) {
|
|
Packit |
e4b6da |
$self->{rw}->request('TP',
|
|
Packit |
e4b6da |
$elem->parent->attr('indent'));
|
|
Packit |
e4b6da |
$templates->push_mode('single-line-mode');
|
|
Packit |
e4b6da |
} elsif($elem->in('TPauto')) {
|
|
Packit |
e4b6da |
} else {
|
|
Packit |
e4b6da |
&illegal_element_handler;
|
|
Packit |
e4b6da |
}
|
|
Packit |
e4b6da |
});
|
|
Packit |
e4b6da |
|
|
Packit |
e4b6da |
$templates->add_rule('TPtag>', sub {
|
|
Packit |
e4b6da |
my ($self, $elem, $templates) = @_;
|
|
Packit |
e4b6da |
if($elem->in('TP')) {
|
|
Packit |
e4b6da |
$templates->pop_mode();
|
|
Packit |
e4b6da |
$self->{rw}->print_ws("\n");
|
|
Packit |
e4b6da |
}
|
|
Packit |
e4b6da |
});
|
|
Packit |
e4b6da |
|
|
Packit |
e4b6da |
$templates->add_rule('TPitem<', sub {
|
|
Packit |
e4b6da |
my ($self, $elem, $templates) = @_;
|
|
Packit |
e4b6da |
|
|
Packit |
e4b6da |
if($elem->in('TP')) {
|
|
Packit |
e4b6da |
} elsif($elem->in('TPauto')) {
|
|
Packit |
e4b6da |
# This is from Bernd Westphal's old patch
|
|
Packit |
e4b6da |
# to docbook2man-sgmlspl.
|
|
Packit |
e4b6da |
|
|
Packit |
e4b6da |
$self->{rw}->escape('kx'); # Save current horiz. position to x
|
|
Packit |
e4b6da |
$self->{rw}->request('if', # if too far left,
|
|
Packit |
e4b6da |
qw{ (\nx>(\n(.l/2)) .nr x (\n(.l/5) });
|
|
Packit |
e4b6da |
# set x to 0.2 * line-length
|
|
Packit |
e4b6da |
$self->{rw}->request("'in", # modify indent, suppress newline
|
|
Packit |
e4b6da |
qw{ \n(.iu+\nxu });
|
|
Packit |
e4b6da |
} else {
|
|
Packit |
e4b6da |
&illegal_element_handler;
|
|
Packit |
e4b6da |
}
|
|
Packit |
e4b6da |
});
|
|
Packit |
e4b6da |
|
|
Packit |
e4b6da |
$templates->add_rule('TPitem>', sub {
|
|
Packit |
e4b6da |
my ($self, $elem, $templates) = @_;
|
|
Packit |
e4b6da |
|
|
Packit |
e4b6da |
if($elem->in('TP')) {
|
|
Packit |
e4b6da |
} elsif($elem->in('TPauto')) {
|
|
Packit |
e4b6da |
$self->{rw}->request("'in", # restore indent
|
|
Packit |
e4b6da |
qw{ \n(.iu-\nxu });
|
|
Packit |
e4b6da |
}
|
|
Packit |
e4b6da |
});
|
|
Packit |
e4b6da |
|
|
Packit |
e4b6da |
|
|
Packit |
e4b6da |
|
|
Packit |
e4b6da |
|
|
Packit |
e4b6da |
##################################################
|
|
Packit |
e4b6da |
#
|
|
Packit |
e4b6da |
# Verbatim sections (disable filling and adjusting)
|
|
Packit |
e4b6da |
#
|
|
Packit |
e4b6da |
##################################################
|
|
Packit |
e4b6da |
|
|
Packit |
e4b6da |
$templates->add_rule('verbatim<', sub {
|
|
Packit |
e4b6da |
my ($self, $elem, $templates) = @_;
|
|
Packit |
e4b6da |
block_start($self, $elem);
|
|
Packit |
e4b6da |
$templates->push_mode('verbatim-mode');
|
|
Packit |
e4b6da |
$self->{rw}->request('nf');
|
|
Packit |
e4b6da |
});
|
|
Packit |
e4b6da |
$templates->add_rule('verbatim>', sub {
|
|
Packit |
e4b6da |
my ($self, $elem, $templates) = @_;
|
|
Packit |
e4b6da |
$templates->pop_mode();
|
|
Packit |
e4b6da |
$self->{rw}->request('fi');
|
|
Packit |
e4b6da |
});
|
|
Packit |
e4b6da |
|
|
Packit |
e4b6da |
|
|
Packit |
e4b6da |
|
|
Packit |
e4b6da |
|
|
Packit |
e4b6da |
##################################################
|
|
Packit |
e4b6da |
#
|
|
Packit |
e4b6da |
# Plain old indent
|
|
Packit |
e4b6da |
#
|
|
Packit |
e4b6da |
##################################################
|
|
Packit |
e4b6da |
|
|
Packit |
e4b6da |
$templates->add_rule('indent<', sub {
|
|
Packit |
e4b6da |
my ($self, $elem, $templates) = @_;
|
|
Packit |
e4b6da |
|
|
Packit |
e4b6da |
$elem->parent->ext->{lastchild} = 'block';
|
|
Packit |
e4b6da |
#block_start($self, $elem);
|
|
Packit |
e4b6da |
#
|
|
Packit |
e4b6da |
my $indent = $elem->attr('indent');
|
|
Packit |
e4b6da |
$self->{rw}->request('RS', $indent);
|
|
Packit |
e4b6da |
});
|
|
Packit |
e4b6da |
$templates->add_rule('indent>', sub {
|
|
Packit |
e4b6da |
my ($self, $elem, $templates) = @_;
|
|
Packit |
e4b6da |
$self->{rw}->request('RE');
|
|
Packit |
e4b6da |
});
|
|
Packit |
e4b6da |
|
|
Packit |
e4b6da |
|
|
Packit |
e4b6da |
|
|
Packit |
e4b6da |
##################################################
|
|
Packit |
e4b6da |
#
|
|
Packit |
e4b6da |
# Vertical spacing
|
|
Packit |
e4b6da |
#
|
|
Packit |
e4b6da |
##################################################
|
|
Packit |
e4b6da |
|
|
Packit |
e4b6da |
$templates->add_rule('sp<', sub {
|
|
Packit |
e4b6da |
my ($self, $elem, $templates) = @_;
|
|
Packit |
e4b6da |
$self->{rw}->request('sp',
|
|
Packit |
e4b6da |
$elem->attr('length'));
|
|
Packit |
e4b6da |
});
|
|
Packit |
e4b6da |
$templates->add_rule('br<', sub {
|
|
Packit |
e4b6da |
my ($self, $elem, $templates) = @_;
|
|
Packit |
e4b6da |
$self->{rw}->request('br');
|
|
Packit |
e4b6da |
});
|
|
Packit |
e4b6da |
|
|
Packit |
e4b6da |
##################################################
|
|
Packit |
e4b6da |
#
|
|
Packit |
e4b6da |
# Tables
|
|
Packit |
e4b6da |
#
|
|
Packit |
e4b6da |
##################################################
|
|
Packit |
e4b6da |
|
|
Packit |
e4b6da |
=head1 Tables
|
|
Packit |
e4b6da |
|
|
Packit |
e4b6da |
db2x_manxml implements CALS tables directly,
|
|
Packit |
e4b6da |
or rather, a XML description of tables that is really
|
|
Packit |
e4b6da |
similar to how DocBook CALS tables work.
|
|
Packit |
e4b6da |
|
|
Packit |
e4b6da |
The differences are:
|
|
Packit |
e4b6da |
|
|
Packit |
e4b6da |
=over 4
|
|
Packit |
e4b6da |
=item *
|
|
Packit |
e4b6da |
Some presentational attributes may not work,
|
|
Packit |
e4b6da |
because they are not supported by tbl.
|
|
Packit |
e4b6da |
|
|
Packit |
e4b6da |
=item *
|
|
Packit |
e4b6da |
Character-based alignment does not work.
|
|
Packit |
e4b6da |
|
|
Packit |
e4b6da |
=item *
|
|
Packit |
e4b6da |
tfoot goes after the tbody, not before.
|
|
Packit |
e4b6da |
This deviation mainly comes from the fact
|
|
Packit |
e4b6da |
that db2x_manxml is stream-based, so content that
|
|
Packit |
e4b6da |
goes at the end of the output has to appear later on
|
|
Packit |
e4b6da |
the input. (It could be worked around by buffering
|
|
Packit |
e4b6da |
the tfoot data, but since tbl does not actually support
|
|
Packit |
e4b6da |
table footers it is not worth the effort.)
|
|
Packit |
e4b6da |
|
|
Packit |
e4b6da |
=item *
|
|
Packit |
e4b6da |
A table cell entry is either an entry or entry element.
|
|
Packit |
e4b6da |
The latter uses tbl's T{ ... T} facility to include
|
|
Packit |
e4b6da |
entire blocks of text, whereas the former contains only inline
|
|
Packit |
e4b6da |
entries. Theoretically all table cell entries can be considered
|
|
Packit |
e4b6da |
as blocks, but for short inline entries using the entry element
|
|
Packit |
e4b6da |
makes the output look nicer.
|
|
Packit |
e4b6da |
|
|
Packit |
e4b6da |
Actually SGML DocBook (but not XML DocBook) makes this
|
|
Packit |
e4b6da |
inline/block distinction also in the DTD content model
|
|
Packit |
e4b6da |
for entry. For a reference, see "pernicious content"
|
|
Packit |
e4b6da |
in the TDG.
|
|
Packit |
e4b6da |
|
|
Packit |
e4b6da |
=item *
|
|
Packit |
e4b6da |
entrytbl is not supported, obviously.
|
|
Packit |
e4b6da |
|
|
Packit |
e4b6da |
=back
|
|
Packit |
e4b6da |
|
|
Packit |
e4b6da |
I am happy to report though that
|
|
Packit |
e4b6da |
both horizontal and vertical spans, and sparse cells[*]
|
|
Packit |
e4b6da |
are supported.
|
|
Packit |
e4b6da |
|
|
Packit |
e4b6da |
[*] That is, specifying that an individual table cell should
|
|
Packit |
e4b6da |
go into a specific column (previously specified by colspec).
|
|
Packit |
e4b6da |
|
|
Packit |
e4b6da |
=cut
|
|
Packit |
e4b6da |
|
|
Packit |
e4b6da |
$templates->add_rule('table<', sub {
|
|
Packit |
e4b6da |
my ($self, $elem, $templates) = @_;
|
|
Packit |
e4b6da |
$self->{rw}->request('TS');
|
|
Packit |
e4b6da |
|
|
Packit |
e4b6da |
my $global_options;
|
|
Packit |
e4b6da |
if($elem->attr('frame') eq ''
|
|
Packit |
e4b6da |
or $elem->attr('frame') eq 'none')
|
|
Packit |
e4b6da |
{
|
|
Packit |
e4b6da |
} elsif($elem->attr('frame') eq 'all') {
|
|
Packit |
e4b6da |
$global_options .= 'allbox ';
|
|
Packit |
e4b6da |
} else {
|
|
Packit |
e4b6da |
$templates->warn_location($elem, "only a frame of 'all' or 'none' is supported");
|
|
Packit |
e4b6da |
}
|
|
Packit |
e4b6da |
|
|
Packit |
e4b6da |
if($elem->attr('pgwide')) {
|
|
Packit |
e4b6da |
$global_options .= 'expand';
|
|
Packit |
e4b6da |
}
|
|
Packit |
e4b6da |
|
|
Packit |
e4b6da |
$self->{rw}->print_ws($global_options . ";\n")
|
|
Packit |
e4b6da |
if defined $global_options;
|
|
Packit |
e4b6da |
|
|
Packit |
e4b6da |
$templates->push_mode('table-mode');
|
|
Packit |
e4b6da |
# Global options line
|
|
Packit |
e4b6da |
});
|
|
Packit |
e4b6da |
$templates->add_rule('table>', sub {
|
|
Packit |
e4b6da |
my ($self, $elem, $templates) = @_;
|
|
Packit |
e4b6da |
$templates->pop_mode();
|
|
Packit |
e4b6da |
$self->{rw}->request('TE');
|
|
Packit |
e4b6da |
});
|
|
Packit |
e4b6da |
|
|
Packit |
e4b6da |
=head2 Algorithm for rendering tables
|
|
Packit |
e4b6da |
|
|
Packit |
e4b6da |
For simplicity in this description I will concentrate on rendering a
|
|
Packit |
e4b6da |
tbody. Rendering thead and tfoot is similar. I will also assume that
|
|
Packit |
e4b6da |
you know how tbl works and its syntax.
|
|
Packit |
e4b6da |
|
|
Packit |
e4b6da |
First, when colspec or spanspec is encountered, the data on the column
|
|
Packit |
e4b6da |
number, alignment, etc. is stored in the tgroup's node ext structure.
|
|
Packit |
e4b6da |
colspecs and spanspecs that occur in thead and tfoot override the ones
|
|
Packit |
e4b6da |
in tgroup, and tbody uses the colspecs and spanspecs that physically
|
|
Packit |
e4b6da |
occur under tgroup, unchanged, as per the CALS specification.
|
|
Packit |
e4b6da |
|
|
Packit |
e4b6da |
The most critical information is the column number. The running column
|
|
Packit |
e4b6da |
number is always kept, and is used whenever a colspec does not specify a
|
|
Packit |
e4b6da |
column number explicitly. (Similarly for entry and entrytbl.)
|
|
Packit |
e4b6da |
|
|
Packit |
e4b6da |
tbl requires that the formatting information for I<all> the table rows
|
|
Packit |
e4b6da |
to come before any of the actual data. However, this information must
|
|
Packit |
e4b6da |
be obtained as each row and cell is processed. (This information mainly
|
|
Packit |
e4b6da |
consists of the length of horizontal or vertical spans, if any, as well
|
|
Packit |
e4b6da |
as text alignment, which may be customized differently for each cell.)
|
|
Packit |
e4b6da |
|
|
Packit |
e4b6da |
So all the rows have to be buffered as they are processed, and then, at
|
|
Packit |
e4b6da |
the end of the table, the formatting information is output, then all the
|
|
Packit |
e4b6da |
buffered rows. A special buffering mode in RoffWriter is used to do
|
|
Packit |
e4b6da |
this.
|
|
Packit |
e4b6da |
|
|
Packit |
e4b6da |
There is no problem with this buffering when processing thead or tfoot,
|
|
Packit |
e4b6da |
since there are usually only one or two lines in those sections. But
|
|
Packit |
e4b6da |
the tbody may have thousands of lines. Even if we don't exactly run out
|
|
Packit |
e4b6da |
of memory, the output would not be very nice because we would have a
|
|
Packit |
e4b6da |
thousand format-lines (the formatting information for one particular
|
|
Packit |
e4b6da |
row) that are nearly identical.
|
|
Packit |
e4b6da |
|
|
Packit |
e4b6da |
Therefore we add a refinement to our procedure: we watch the
|
|
Packit |
e4b6da |
format-lines, and if they are the same for the last few rows we flush
|
|
Packit |
e4b6da |
our buffer. If we have a different format-line coming later in our
|
|
Packit |
e4b6da |
input, we use the table continuation request (C<.T&>) to change it (in
|
|
Packit |
e4b6da |
blocks). So our output looks like this:
|
|
Packit |
e4b6da |
|
|
Packit |
e4b6da |
l l l l # Format lines
|
|
Packit |
e4b6da |
l l l s
|
|
Packit |
e4b6da |
l l l l. # All the lines from the third line on have this format
|
|
Packit |
e4b6da |
Data Data Data Data
|
|
Packit |
e4b6da |
Data Data Data
|
|
Packit |
e4b6da |
Data Data Data Data
|
|
Packit |
e4b6da |
Data Data Data Data
|
|
Packit |
e4b6da |
Data Data Data Data
|
|
Packit |
e4b6da |
Data Data Data Data
|
|
Packit |
e4b6da |
Data Data Data Data
|
|
Packit |
e4b6da |
Data Data Data Data
|
|
Packit |
e4b6da |
.T&
|
|
Packit |
e4b6da |
l l l s
|
|
Packit |
e4b6da |
l l l l.
|
|
Packit |
e4b6da |
...
|
|
Packit |
e4b6da |
Data
|
|
Packit |
e4b6da |
...
|
|
Packit |
e4b6da |
.T&
|
|
Packit |
e4b6da |
l l l r
|
|
Packit |
e4b6da |
l l l l.
|
|
Packit |
e4b6da |
...
|
|
Packit |
e4b6da |
Data
|
|
Packit |
e4b6da |
...
|
|
Packit |
e4b6da |
|
|
Packit |
e4b6da |
Of course, theoretically we could use C<.T&> for each any every row, but
|
|
Packit |
e4b6da |
this bloats the output.
|
|
Packit |
e4b6da |
|
|
Packit |
e4b6da |
As I have mentioned, the format-lines contain span information.
|
|
Packit |
e4b6da |
Actually cell spans can be handled fairly easily with an
|
|
Packit |
e4b6da |
iterative/imperative algorithm. (As opposed to a functional/recursive
|
|
Packit |
e4b6da |
one, which makes it a pain to implement in XSLT, and so we do it here
|
|
Packit |
e4b6da |
instead.)
|
|
Packit |
e4b6da |
|
|
Packit |
e4b6da |
First, there is a default format-line, which is constructed with the
|
|
Packit |
e4b6da |
help of the colspec information. The format-line is represented as a
|
|
Packit |
e4b6da |
flat array with each element corresponding to one column, in order from
|
|
Packit |
e4b6da |
left-to-right (assume LTR natural languages here, since roff does not
|
|
Packit |
e4b6da |
support other writing directions). The list elements are simply strings
|
|
Packit |
e4b6da |
like "l", "r", "c", etc. i.e. the same sort of column specifiers as
|
|
Packit |
e4b6da |
used in tbl. To make a format-line in the output it is only needed to
|
|
Packit |
e4b6da |
C<join> this list.
|
|
Packit |
e4b6da |
|
|
Packit |
e4b6da |
When a table entry comes along and specifies some horizontal span, the
|
|
Packit |
e4b6da |
default format-line is taken as the initial template for the current
|
|
Packit |
e4b6da |
line's format-line, and the columns in this format-line array that are
|
|
Packit |
e4b6da |
affected by the span are assigned a different specifier (something like
|
|
Packit |
e4b6da |
"s"). The newly-changed format-line is the one that is used for the
|
|
Packit |
e4b6da |
current line.
|
|
Packit |
e4b6da |
|
|
Packit |
e4b6da |
Of course, when there is a horizontal span, care must be taken that
|
|
Packit |
e4b6da |
we skip over the correct number of 'physical' cells.
|
|
Packit |
e4b6da |
|
|
Packit |
e4b6da |
For vertical spans, the format-line handling is the same, except with a
|
|
Packit |
e4b6da |
different tbl column specifier ("^"). Vertical spanning does require
|
|
Packit |
e4b6da |
that subsequent rows know about the vertically spanning cells and
|
|
Packit |
e4b6da |
reserve space for them: this is easily handled with another flat array
|
|
Packit |
e4b6da |
in a similar fashion to the format-line array. Each element of the
|
|
Packit |
e4b6da |
current_vspans array would hold a non-negative integer indicating how
|
|
Packit |
e4b6da |
many more rows to reserve for each column. (Zero means there is no
|
|
Packit |
e4b6da |
vertical span for that column, naturally.) Once each row is finished,
|
|
Packit |
e4b6da |
each number of this list is decremented by one, unless it is zero, in
|
|
Packit |
e4b6da |
which it stays at zero. Whenever a table cell requests vertical span, the
|
|
Packit |
e4b6da |
appropriate column of the current_vspans array is re-assigned to.
|
|
Packit |
e4b6da |
|
|
Packit |
e4b6da |
The current_vspans array is also consulted when advancing columns
|
|
Packit |
e4b6da |
(as one row's table cells are processed) so that the current column
|
|
Packit |
e4b6da |
is not advanced in the middle of some vertical span from a previous row.
|
|
Packit |
e4b6da |
|
|
Packit |
e4b6da |
=cut
|
|
Packit |
e4b6da |
|
|
Packit |
e4b6da |
$templates->add_rule('tgroup<', 'table-mode', sub {
|
|
Packit |
e4b6da |
my ($self, $elem, $templates) = @_;
|
|
Packit |
e4b6da |
|
|
Packit |
e4b6da |
$elem->ext->{total_cols} = $elem->attr('cols');
|
|
Packit |
e4b6da |
|
|
Packit |
e4b6da |
if($elem->attr('cols') !~ /^\d+$/
|
|
Packit |
e4b6da |
or $elem->attr('cols') < 1)
|
|
Packit |
e4b6da |
{
|
|
Packit |
e4b6da |
$templates->warn_location($elem, "fatal error: invalid number of columns for table");
|
|
Packit |
e4b6da |
}
|
|
Packit |
e4b6da |
|
|
Packit |
e4b6da |
$elem->ext->{column_data} = [];
|
|
Packit |
e4b6da |
|
|
Packit |
e4b6da |
$elem->ext->{colnames} = {};
|
|
Packit |
e4b6da |
$elem->ext->{spannames} = {};
|
|
Packit |
e4b6da |
|
|
Packit |
e4b6da |
$elem->ext->{colspec_current_colnum} = 0;
|
|
Packit |
e4b6da |
|
|
Packit |
e4b6da |
$elem->ext->{align} = $elem->attr('align') || 'left';
|
|
Packit |
e4b6da |
$elem->ext->{rowsep} =
|
|
Packit |
e4b6da |
( (defined $elem->attr('rowsep'))?
|
|
Packit |
e4b6da |
$elem->attr('rowsep') :
|
|
Packit |
e4b6da |
$elem->parent->attr('rowsep') );
|
|
Packit |
e4b6da |
$elem->ext->{colsep} =
|
|
Packit |
e4b6da |
( (defined $elem->attr('colsep'))?
|
|
Packit |
e4b6da |
$elem->attr('colsep') :
|
|
Packit |
e4b6da |
$elem->parent->attr('colsep') );
|
|
Packit |
e4b6da |
});
|
|
Packit |
e4b6da |
|
|
Packit |
e4b6da |
$templates->add_rule('tgroup>', 'table-mode', sub {});
|
|
Packit |
e4b6da |
|
|
Packit |
e4b6da |
$templates->add_rule('colspec<', 'table-mode', sub {
|
|
Packit |
e4b6da |
my ($self, $elem, $templates) = @_;
|
|
Packit |
e4b6da |
my $t_elem = $elem->parent;
|
|
Packit |
e4b6da |
my $tgroup = $t_elem->parent;
|
|
Packit |
e4b6da |
|
|
Packit |
e4b6da |
if($t_elem->name ne 'tgroup') {
|
|
Packit |
e4b6da |
$t_elem->ext->{total_cols} = $tgroup->ext->{total_cols};
|
|
Packit |
e4b6da |
$t_elem->ext->{column_data} = [];
|
|
Packit |
e4b6da |
$t_elem->ext->{colnames} = [];
|
|
Packit |
e4b6da |
$t_elem->ext->{spannames} = {};
|
|
Packit |
e4b6da |
}
|
|
Packit |
e4b6da |
|
|
Packit |
e4b6da |
my $colnum;
|
|
Packit |
e4b6da |
if($elem->attr('colnum')) {
|
|
Packit |
e4b6da |
$colnum = $elem->attr('colnum');
|
|
Packit |
e4b6da |
if($colnum !~ /^\d+$/ or $colnum < 1) {
|
|
Packit |
e4b6da |
$templates->warn_location($elem, "invalid column number --- ignoring\n");
|
|
Packit |
e4b6da |
$colnum = $t_elem->ext->{colspec_current_colnum} + 1;
|
|
Packit |
e4b6da |
} elsif($colnum <= $t_elem->ext->{colspec_current_colnum}) {
|
|
Packit |
e4b6da |
$templates->warn_location($elem, "column numbers of colspecs are not given in an increasing sequence\n");
|
|
Packit |
e4b6da |
}
|
|
Packit |
e4b6da |
} else {
|
|
Packit |
e4b6da |
$colnum = $t_elem->ext->{colspec_current_colnum} + 1;
|
|
Packit |
e4b6da |
}
|
|
Packit |
e4b6da |
|
|
Packit |
e4b6da |
if($colnum > $t_elem->ext->{total_cols}) {
|
|
Packit |
e4b6da |
$templates->warn_location($elem, "column number exceeds total number of columns --- ignoring this colspec\n");
|
|
Packit |
e4b6da |
return;
|
|
Packit |
e4b6da |
}
|
|
Packit |
e4b6da |
|
|
Packit |
e4b6da |
$t_elem->ext->{colspec_current_colnum} = $colnum;
|
|
Packit |
e4b6da |
|
|
Packit |
e4b6da |
my $column_data_entry =
|
|
Packit |
e4b6da |
{ colwidth => $elem->attr('colwidth'),
|
|
Packit |
e4b6da |
colname => $elem->attr('colname'),
|
|
Packit |
e4b6da |
colnum => $colnum,
|
|
Packit |
e4b6da |
|
|
Packit |
e4b6da |
rowsep => $elem->attr('rowsep'),
|
|
Packit |
e4b6da |
colsep => $elem->attr('colsep'),
|
|
Packit |
e4b6da |
align => $elem->attr('align') || $tgroup->ext->{align}
|
|
Packit |
e4b6da |
};
|
|
Packit |
e4b6da |
|
|
Packit |
e4b6da |
$t_elem->ext->{column_data}->[$colnum-1] = $column_data_entry;
|
|
Packit |
e4b6da |
|
|
Packit |
e4b6da |
$t_elem->ext->{colnames}->{$elem->attr('colname')}
|
|
Packit |
e4b6da |
= $column_data_entry
|
|
Packit |
e4b6da |
if $elem->attr('colname') ne '';
|
|
Packit |
e4b6da |
});
|
|
Packit |
e4b6da |
|
|
Packit |
e4b6da |
$templates->add_rule('spanspec<', 'table-mode', sub {
|
|
Packit |
e4b6da |
my ($self, $elem, $templates) = @_;
|
|
Packit |
e4b6da |
my $t_elem = $elem->parent;
|
|
Packit |
e4b6da |
|
|
Packit |
e4b6da |
$t_elem->ext->{spannames}->{$elem->attr('spanname')}
|
|
Packit |
e4b6da |
= {
|
|
Packit |
e4b6da |
start => $t_elem->ext->{colnames}->{$elem->attr('namest')},
|
|
Packit |
e4b6da |
end => $t_elem->ext->{colnames}->{$elem->attr('nameend')},
|
|
Packit |
e4b6da |
|
|
Packit |
e4b6da |
rowsep => $elem->attr('rowsep'),
|
|
Packit |
e4b6da |
colsep => $elem->attr('colsep'),
|
|
Packit |
e4b6da |
align => $elem->attr('align') ||
|
|
Packit |
e4b6da |
$t_elem->ext->{colnames}->{$elem->attr('namest')}->{align}
|
|
Packit |
e4b6da |
};
|
|
Packit |
e4b6da |
});
|
|
Packit |
e4b6da |
|
|
Packit |
e4b6da |
sub tbl_build_format_line_helper
|
|
Packit |
e4b6da |
{
|
|
Packit |
e4b6da |
my ($self, $elem) = @_;
|
|
Packit |
e4b6da |
my $tgroup = $elem->parent;
|
|
Packit |
e4b6da |
|
|
Packit |
e4b6da |
my @format_line = ();
|
|
Packit |
e4b6da |
|
|
Packit |
e4b6da |
for(my $i = 0; $i < $elem->ext->{total_cols}; $i++)
|
|
Packit |
e4b6da |
{
|
|
Packit |
e4b6da |
my $cd = $elem->ext->{column_data}->[$i];
|
|
Packit |
e4b6da |
|
|
Packit |
e4b6da |
my $f = tbl_align_to_tbl(
|
|
Packit |
e4b6da |
(defined $cd ? $cd->{align} : $tgroup->ext->{align}));
|
|
Packit |
e4b6da |
$f .= tbl_colwidth_to_tbl($cd->{colwidth})
|
|
Packit |
e4b6da |
if (defined $cd and defined $cd->{colwidth});
|
|
Packit |
e4b6da |
$f .= ' |'
|
|
Packit |
e4b6da |
if ((defined $cd and $cd->{colsep}) or
|
|
Packit |
e4b6da |
$tgroup->ext->{colsep})
|
|
Packit |
e4b6da |
and ($i != $elem->ext->{total_cols}-1);
|
|
Packit |
e4b6da |
|
|
Packit |
e4b6da |
push(@format_line, $f);
|
|
Packit |
e4b6da |
}
|
|
Packit |
e4b6da |
|
|
Packit |
e4b6da |
return \@format_line;
|
|
Packit |
e4b6da |
}
|
|
Packit |
e4b6da |
|
|
Packit |
e4b6da |
sub tbl_colwidth_to_tbl
|
|
Packit |
e4b6da |
{
|
|
Packit |
e4b6da |
my ($s) = @_;
|
|
Packit |
e4b6da |
# FIXME!
|
|
Packit |
e4b6da |
return '';
|
|
Packit |
e4b6da |
}
|
|
Packit |
e4b6da |
|
|
Packit |
e4b6da |
sub tbl_build_format_line
|
|
Packit |
e4b6da |
{
|
|
Packit |
e4b6da |
my ($self, $elem) = @_;
|
|
Packit |
e4b6da |
|
|
Packit |
e4b6da |
if($elem->name eq 'tbody'
|
|
Packit |
e4b6da |
or !defined $elem->ext->{column_data})
|
|
Packit |
e4b6da |
{
|
|
Packit |
e4b6da |
foreach my $x (qw(total_cols column_data colnames spannames)) {
|
|
Packit |
e4b6da |
$elem->ext->{$x} = $elem->parent->ext->{$x};
|
|
Packit |
e4b6da |
}
|
|
Packit |
e4b6da |
}
|
|
Packit |
e4b6da |
|
|
Packit |
e4b6da |
$elem->ext->{default_format_line} =
|
|
Packit |
e4b6da |
tbl_build_format_line_helper($self, $elem);
|
|
Packit |
e4b6da |
|
|
Packit |
e4b6da |
my @vspans = (0) x $elem->ext->{total_cols};
|
|
Packit |
e4b6da |
$elem->ext->{current_vspans} = \@vspans;
|
|
Packit |
e4b6da |
|
|
Packit |
e4b6da |
my @vspan_template_format = ('^') x $elem->ext->{total_cols};
|
|
Packit |
e4b6da |
$elem->ext->{vspan_template_format} = \@vspan_template_format;
|
|
Packit |
e4b6da |
|
|
Packit |
e4b6da |
my @current_rowseps = ('^') x $elem->ext->{total_cols};
|
|
Packit |
e4b6da |
$elem->ext->{current_rowseps} = \@current_rowseps;
|
|
Packit |
e4b6da |
}
|
|
Packit |
e4b6da |
|
|
Packit |
e4b6da |
|
|
Packit |
e4b6da |
sub t_elem_start_handler { my ($self, $elem, $templates) = @_;
|
|
Packit |
e4b6da |
tbl_format_line_buffer_start($elem, $self->{rw}); }
|
|
Packit |
e4b6da |
sub t_elem_end_handler { my ($self, $elem, $templates) = @_;
|
|
Packit |
e4b6da |
tbl_format_line_buffer_flush($elem, $self->{rw}); }
|
|
Packit |
e4b6da |
|
|
Packit |
e4b6da |
$templates->add_rule('thead<', 'table-mode', \&t_elem_start_handler);
|
|
Packit |
e4b6da |
$templates->add_rule('thead>', 'table-mode', \&t_elem_end_handler);
|
|
Packit |
e4b6da |
$templates->add_rule('tfoot<', 'table-mode', \&t_elem_start_handler);
|
|
Packit |
e4b6da |
$templates->add_rule('tfoot>', 'table-mode', \&t_elem_end_handler);
|
|
Packit |
e4b6da |
$templates->add_rule('tbody<', 'table-mode', \&t_elem_start_handler);
|
|
Packit |
e4b6da |
$templates->add_rule('tbody>', 'table-mode', \&t_elem_end_handler);
|
|
Packit |
e4b6da |
|
|
Packit |
e4b6da |
sub tbl_advance_column
|
|
Packit |
e4b6da |
{
|
|
Packit |
e4b6da |
my ($row, $rw, $new_colnum, $relative_advance) = @_;
|
|
Packit |
e4b6da |
|
|
Packit |
e4b6da |
my $old_colnum = $row->ext->{current_colnum};
|
|
Packit |
e4b6da |
my $total_cols = $row->parent->ext->{total_cols};
|
|
Packit |
e4b6da |
|
|
Packit |
e4b6da |
if($relative_advance) {
|
|
Packit |
e4b6da |
my $vspans = $row->parent->ext->{current_vspans};
|
|
Packit |
e4b6da |
for($new_colnum = $old_colnum + $relative_advance;
|
|
Packit |
e4b6da |
$new_colnum <= $total_cols && ($vspans->[$new_colnum - 1] > 0);
|
|
Packit |
e4b6da |
$new_colnum++)
|
|
Packit |
e4b6da |
{}
|
|
Packit |
e4b6da |
}
|
|
Packit |
e4b6da |
elsif($new_colnum == -1) {
|
|
Packit |
e4b6da |
$new_colnum = $total_cols + 1;
|
|
Packit |
e4b6da |
}
|
|
Packit |
e4b6da |
|
|
Packit |
e4b6da |
$row->ext->{current_colnum} = $new_colnum;
|
|
Packit |
e4b6da |
|
|
Packit |
e4b6da |
$new_colnum = $total_cols if $new_colnum > $total_cols;
|
|
Packit |
e4b6da |
$old_colnum = 1 if $old_colnum == 0;
|
|
Packit |
e4b6da |
|
|
Packit |
e4b6da |
$rw->print("\t" x ($new_colnum - $old_colnum));
|
|
Packit |
e4b6da |
}
|
|
Packit |
e4b6da |
|
|
Packit |
e4b6da |
|
|
Packit |
e4b6da |
$templates->add_rule('row<', 'table-mode', sub {
|
|
Packit |
e4b6da |
my ($self, $elem, $templates) = @_;
|
|
Packit |
e4b6da |
my $t_elem = $elem->parent;
|
|
Packit |
e4b6da |
my $tgroup = $t_elem->parent;
|
|
Packit |
e4b6da |
|
|
Packit |
e4b6da |
if(! $t_elem->ext->{num_rows}++) {
|
|
Packit |
e4b6da |
tbl_build_format_line($self, $t_elem);
|
|
Packit |
e4b6da |
}
|
|
Packit |
e4b6da |
|
|
Packit |
e4b6da |
if(defined $tgroup->ext->{last_rowseps}) {
|
|
Packit |
e4b6da |
# tbl has this funny (seemingly undocumented)
|
|
Packit |
e4b6da |
# behavior where if a format line is all underscores
|
|
Packit |
e4b6da |
# then the corresponding (blank) data line _must_ be omitted.
|
|
Packit |
e4b6da |
$self->{rw}->print("\n")
|
|
Packit |
e4b6da |
if grep(/[^_]/, @{$tgroup->ext->{last_rowseps}});
|
|
Packit |
e4b6da |
|
|
Packit |
e4b6da |
if($t_elem->name ne 'tbody') {
|
|
Packit |
e4b6da |
tbl_format_line_buffer_push($t_elem,
|
|
Packit |
e4b6da |
$tgroup->ext->{last_rowseps});
|
|
Packit |
e4b6da |
} else {
|
|
Packit |
e4b6da |
tbl_format_line_buffer_push_ex($t_elem, $self->{rw},
|
|
Packit |
e4b6da |
$tgroup->ext->{last_rowseps});
|
|
Packit |
e4b6da |
}
|
|
Packit |
e4b6da |
|
|
Packit |
e4b6da |
$tgroup->ext->{last_rowseps} = undef;
|
|
Packit |
e4b6da |
}
|
|
Packit |
e4b6da |
|
|
Packit |
e4b6da |
$elem->ext->{current_colnum} = 0;
|
|
Packit |
e4b6da |
tbl_advance_column($elem, $self->{rw}, 0, 1);
|
|
Packit |
e4b6da |
$elem->ext->{this_format_line} =
|
|
Packit |
e4b6da |
list_copy($t_elem->ext->{default_format_line});
|
|
Packit |
e4b6da |
|
|
Packit |
e4b6da |
tbl_copy_vspan_template_format(
|
|
Packit |
e4b6da |
$elem->ext->{this_format_line},
|
|
Packit |
e4b6da |
$t_elem->ext->{current_vspans},
|
|
Packit |
e4b6da |
$t_elem->ext->{vspan_template_format});
|
|
Packit |
e4b6da |
|
|
Packit |
e4b6da |
for(my $i = 0; $i < @{$t_elem->ext->{current_vspans}}; $i++)
|
|
Packit |
e4b6da |
{
|
|
Packit |
e4b6da |
if($t_elem->ext->{current_vspans}->[$i] > 0) {
|
|
Packit |
e4b6da |
} elsif(defined $elem->attr('rowsep')) {
|
|
Packit |
e4b6da |
$t_elem->ext->{current_rowseps}->[$i] =
|
|
Packit |
e4b6da |
( $elem->attr('rowsep') ? '_' : '^' );
|
|
Packit |
e4b6da |
} elsif(defined $t_elem->ext->{column_data}->[$i]) {
|
|
Packit |
e4b6da |
$t_elem->ext->{current_rowseps}->[$i] =
|
|
Packit |
e4b6da |
( $t_elem->ext->{column_data}->[$i]->{rowsep} ? '_' : '^' );
|
|
Packit |
e4b6da |
} else {
|
|
Packit |
e4b6da |
$t_elem->ext->{current_rowseps}->[$i] =
|
|
Packit |
e4b6da |
( $tgroup->ext->{rowsep} ? '_' : '^' );
|
|
Packit |
e4b6da |
}
|
|
Packit |
e4b6da |
}
|
|
Packit |
e4b6da |
});
|
|
Packit |
e4b6da |
|
|
Packit |
e4b6da |
sub tbl_copy_vspan_template_format
|
|
Packit |
e4b6da |
{
|
|
Packit |
e4b6da |
my ($format_line, $vspans, $vspan_template_format) = @_;
|
|
Packit |
e4b6da |
for(my $i = 0; $i < @$vspans; $i++)
|
|
Packit |
e4b6da |
{
|
|
Packit |
e4b6da |
$format_line->[$i] = $vspan_template_format->[$i]
|
|
Packit |
e4b6da |
if $vspans->[$i] > 0;
|
|
Packit |
e4b6da |
}
|
|
Packit |
e4b6da |
|
|
Packit |
e4b6da |
return $format_line;
|
|
Packit |
e4b6da |
}
|
|
Packit |
e4b6da |
|
|
Packit |
e4b6da |
$templates->add_rule('row>', 'table-mode', sub {
|
|
Packit |
e4b6da |
my ($self, $elem, $templates) = @_;
|
|
Packit |
e4b6da |
my $t_elem = $elem->parent;
|
|
Packit |
e4b6da |
my $tgroup = $t_elem->parent;
|
|
Packit |
e4b6da |
|
|
Packit |
e4b6da |
tbl_advance_column($elem, $self->{rw}, -1);
|
|
Packit |
e4b6da |
|
|
Packit |
e4b6da |
$self->{rw}->print_ws("\n");
|
|
Packit |
e4b6da |
|
|
Packit |
e4b6da |
if($t_elem->name ne 'tbody') {
|
|
Packit |
e4b6da |
tbl_format_line_buffer_push($t_elem, $elem->ext->{this_format_line});
|
|
Packit |
e4b6da |
} else {
|
|
Packit |
e4b6da |
tbl_format_line_buffer_push_ex($t_elem, $self->{rw},
|
|
Packit |
e4b6da |
$elem->ext->{this_format_line});
|
|
Packit |
e4b6da |
}
|
|
Packit |
e4b6da |
|
|
Packit |
e4b6da |
foreach my $c (@{$t_elem->ext->{current_vspans}}) {
|
|
Packit |
e4b6da |
$c-- if $c > 0;
|
|
Packit |
e4b6da |
}
|
|
Packit |
e4b6da |
|
|
Packit |
e4b6da |
my @current_rowseps;
|
|
Packit |
e4b6da |
for(my $i = 0; $i < $t_elem->ext->{total_cols}; $i++) {
|
|
Packit |
e4b6da |
$current_rowseps[$i] =
|
|
Packit |
e4b6da |
($t_elem->ext->{current_vspans}->[$i] > 0 ?
|
|
Packit |
e4b6da |
'^' :
|
|
Packit |
e4b6da |
$t_elem->ext->{current_rowseps}->[$i]);
|
|
Packit |
e4b6da |
}
|
|
Packit |
e4b6da |
|
|
Packit |
e4b6da |
# We don't output the borders yet, because they are supposed
|
|
Packit |
e4b6da |
# to be separators and we do not want one on the last row.
|
|
Packit |
e4b6da |
# So output them on the next row.
|
|
Packit |
e4b6da |
$tgroup->ext->{last_rowseps} = \@current_rowseps
|
|
Packit |
e4b6da |
if grep { $_ eq '_' } @current_rowseps;
|
|
Packit |
e4b6da |
});
|
|
Packit |
e4b6da |
|
|
Packit |
e4b6da |
# Compare two lists (given as references),
|
|
Packit |
e4b6da |
# checking they have the same number of elements
|
|
Packit |
e4b6da |
# and each corresponding element is string-wise equal.
|
|
Packit |
e4b6da |
#
|
|
Packit |
e4b6da |
sub list_eq
|
|
Packit |
e4b6da |
{
|
|
Packit |
e4b6da |
my ($a, $b) = @_;
|
|
Packit |
e4b6da |
return 0 if (!defined $a or !defined $b or @$a != @$b);
|
|
Packit |
e4b6da |
|
|
Packit |
e4b6da |
for(my $i = 0; $i < @$a; $i++) {
|
|
Packit |
e4b6da |
return 0 if $a->[$i] ne $b->[$i];
|
|
Packit |
e4b6da |
}
|
|
Packit |
e4b6da |
|
|
Packit |
e4b6da |
return 1;
|
|
Packit |
e4b6da |
}
|
|
Packit |
e4b6da |
|
|
Packit |
e4b6da |
# Shallow-copy a list, given a reference,
|
|
Packit |
e4b6da |
# into a new list, with a new reference.
|
|
Packit |
e4b6da |
#
|
|
Packit |
e4b6da |
sub list_copy
|
|
Packit |
e4b6da |
{
|
|
Packit |
e4b6da |
if(wantarray) {
|
|
Packit |
e4b6da |
return map { my @x = @$_; \@x } @_;
|
|
Packit |
e4b6da |
} else {
|
|
Packit |
e4b6da |
my @x = @{$_[0]};
|
|
Packit |
e4b6da |
return \@x;
|
|
Packit |
e4b6da |
}
|
|
Packit |
e4b6da |
}
|
|
Packit |
e4b6da |
|
|
Packit |
e4b6da |
sub tbl_format_line_buffer_start
|
|
Packit |
e4b6da |
{
|
|
Packit |
e4b6da |
my ($t_elem, $rw) = @_;
|
|
Packit |
e4b6da |
$t_elem->ext->{format_lines} = [];
|
|
Packit |
e4b6da |
$t_elem->ext->{same_format_lines} = 0;
|
|
Packit |
e4b6da |
$t_elem->ext->{current_format_line} = undef;
|
|
Packit |
e4b6da |
|
|
Packit |
e4b6da |
$rw->buffer_on();
|
|
Packit |
e4b6da |
}
|
|
Packit |
e4b6da |
|
|
Packit |
e4b6da |
sub tbl_format_line_buffer_push
|
|
Packit |
e4b6da |
{
|
|
Packit |
e4b6da |
my ($t_elem, $format_line) = @_;
|
|
Packit |
e4b6da |
my $format_lines = $t_elem->ext->{format_lines};
|
|
Packit |
e4b6da |
|
|
Packit |
e4b6da |
if(scalar(@$format_lines) != 0 and
|
|
Packit |
e4b6da |
list_eq(
|
|
Packit |
e4b6da |
$format_lines->[-1],
|
|
Packit |
e4b6da |
$format_line))
|
|
Packit |
e4b6da |
{
|
|
Packit |
e4b6da |
$t_elem->ext->{same_format_lines}++;
|
|
Packit |
e4b6da |
} else {
|
|
Packit |
e4b6da |
$t_elem->ext->{same_format_lines} = 0;
|
|
Packit |
e4b6da |
}
|
|
Packit |
e4b6da |
|
|
Packit |
e4b6da |
push(@$format_lines, $format_line);
|
|
Packit |
e4b6da |
|
|
Packit |
e4b6da |
}
|
|
Packit |
e4b6da |
|
|
Packit |
e4b6da |
sub tbl_format_line_buffer_flush
|
|
Packit |
e4b6da |
{
|
|
Packit |
e4b6da |
my ($t_elem, $rw, $n) = @_;
|
|
Packit |
e4b6da |
my $format_lines = $t_elem->ext->{format_lines};
|
|
Packit |
e4b6da |
my $buffered_rows = $rw->buffer_off();
|
|
Packit |
e4b6da |
|
|
Packit |
e4b6da |
$t_elem->ext->{same_format_lines} = 0;
|
|
Packit |
e4b6da |
$t_elem->ext->{current_format_line} = undef;
|
|
Packit |
e4b6da |
|
|
Packit |
e4b6da |
return if @$format_lines == 0;
|
|
Packit |
e4b6da |
|
|
Packit |
e4b6da |
$rw->request('T&')
|
|
Packit |
e4b6da |
if $t_elem->parent->parent->ext->{'T&'}++;
|
|
Packit |
e4b6da |
|
|
Packit |
e4b6da |
$rw->print(
|
|
Packit |
e4b6da |
join("\n",
|
|
Packit |
e4b6da |
map { join(' ', @$_) } @$format_lines[0 .. ($#$format_lines - $n)])
|
|
Packit |
e4b6da |
. ".\n");
|
|
Packit |
e4b6da |
|
|
Packit |
e4b6da |
$rw->write($buffered_rows);
|
|
Packit |
e4b6da |
|
|
Packit |
e4b6da |
$t_elem->ext->{format_lines} = [];
|
|
Packit |
e4b6da |
}
|
|
Packit |
e4b6da |
|
|
Packit |
e4b6da |
sub tbl_format_line_buffer_push_ex
|
|
Packit |
e4b6da |
{
|
|
Packit |
e4b6da |
my ($t_elem, $rw, $format_line) = @_;
|
|
Packit |
e4b6da |
|
|
Packit |
e4b6da |
if(defined $t_elem->ext->{current_format_line})
|
|
Packit |
e4b6da |
{
|
|
Packit |
e4b6da |
if(list_eq($t_elem->ext->{current_format_line}, $format_line)) {
|
|
Packit |
e4b6da |
$rw->write($rw->buffer_off());
|
|
Packit |
e4b6da |
$rw->buffer_on();
|
|
Packit |
e4b6da |
}
|
|
Packit |
e4b6da |
else {
|
|
Packit |
e4b6da |
$t_elem->ext->{format_lines} = [];
|
|
Packit |
e4b6da |
$t_elem->ext->{same_format_lines} = 0;
|
|
Packit |
e4b6da |
$t_elem->ext->{current_format_line} = undef;
|
|
Packit |
e4b6da |
|
|
Packit |
e4b6da |
tbl_format_line_buffer_push($t_elem, $format_line);
|
|
Packit |
e4b6da |
}
|
|
Packit |
e4b6da |
}
|
|
Packit |
e4b6da |
else {
|
|
Packit |
e4b6da |
tbl_format_line_buffer_push($t_elem, $format_line);
|
|
Packit |
e4b6da |
if($t_elem->ext->{same_format_lines} == 3) {
|
|
Packit |
e4b6da |
tbl_format_line_buffer_flush($t_elem, $rw,
|
|
Packit |
e4b6da |
$t_elem->ext->{same_format_lines});
|
|
Packit |
e4b6da |
$t_elem->ext->{current_format_line} = $format_line;
|
|
Packit |
e4b6da |
|
|
Packit |
e4b6da |
$rw->buffer_on();
|
|
Packit |
e4b6da |
}
|
|
Packit |
e4b6da |
}
|
|
Packit |
e4b6da |
}
|
|
Packit |
e4b6da |
|
|
Packit |
e4b6da |
sub tbl_align_to_tbl {
|
|
Packit |
e4b6da |
my ($align) = @_;
|
|
Packit |
e4b6da |
if ($align eq 'left') { return 'l'; }
|
|
Packit |
e4b6da |
elsif($align eq 'right') { return 'r'; }
|
|
Packit |
e4b6da |
elsif($align eq 'center') { return 'c'; }
|
|
Packit |
e4b6da |
else { return 'l' }
|
|
Packit |
e4b6da |
}
|
|
Packit |
e4b6da |
|
|
Packit |
e4b6da |
|
|
Packit |
e4b6da |
sub tbl_entry_start_handler {
|
|
Packit |
e4b6da |
my ($self, $elem, $templates) = @_;
|
|
Packit |
e4b6da |
my $row = $elem->parent;
|
|
Packit |
e4b6da |
my $t_elem = $elem->parent->parent;
|
|
Packit |
e4b6da |
my $tgroup = $t_elem->parent;
|
|
Packit |
e4b6da |
|
|
Packit |
e4b6da |
my $start_col; my $end_col;
|
|
Packit |
e4b6da |
my $align; my $colsep;
|
|
Packit |
e4b6da |
|
|
Packit |
e4b6da |
if($elem->attr('colname')) {
|
|
Packit |
e4b6da |
$start_col = $t_elem->{colnames}->{$elem->attr('colname')};
|
|
Packit |
e4b6da |
} elsif($elem->attr('spanname')) {
|
|
Packit |
e4b6da |
my $span = $t_elem->{spannames}->{$elem->attr('spanname')};
|
|
Packit |
e4b6da |
$start_col = $span->{start};
|
|
Packit |
e4b6da |
$end_col = $span->{end};
|
|
Packit |
e4b6da |
$align = $span->{align};
|
|
Packit |
e4b6da |
$colsep = $span->{colsep};
|
|
Packit |
e4b6da |
} elsif($elem->attr('namest')) {
|
|
Packit |
e4b6da |
$start_col = $t_elem->{colnames}->{$elem->attr('namest')};
|
|
Packit |
e4b6da |
$end_col = $t_elem->{colnames}->{$elem->attr('nameend')};
|
|
Packit |
e4b6da |
}
|
|
Packit |
e4b6da |
|
|
Packit |
e4b6da |
$align = $elem->attr('align')
|
|
Packit |
e4b6da |
if defined $elem->attr('align');
|
|
Packit |
e4b6da |
$colsep = $elem->attr('colsep')
|
|
Packit |
e4b6da |
if defined $elem->attr('colsep');
|
|
Packit |
e4b6da |
|
|
Packit |
e4b6da |
|
|
Packit |
e4b6da |
if(defined $start_col) {
|
|
Packit |
e4b6da |
tbl_advance_column($row, $self->{rw}, $start_col->{colnum});
|
|
Packit |
e4b6da |
}
|
|
Packit |
e4b6da |
|
|
Packit |
e4b6da |
if(defined $elem->attr('rowsep')) {
|
|
Packit |
e4b6da |
$t_elem->ext->{current_rowseps}->[$row->ext->{current_colnum} - 1] =
|
|
Packit |
e4b6da |
( $elem->attr('rowsep') ? '_' : '^' );
|
|
Packit |
e4b6da |
}
|
|
Packit |
e4b6da |
|
|
Packit |
e4b6da |
if(defined $align) {
|
|
Packit |
e4b6da |
my $t = tbl_align_to_tbl($align);
|
|
Packit |
e4b6da |
$row->ext->{this_format_line}->[$row->ext->{current_colnum} - 1]
|
|
Packit |
e4b6da |
=~ s/^[lrc]/$t/;
|
|
Packit |
e4b6da |
}
|
|
Packit |
e4b6da |
if(defined $colsep) {
|
|
Packit |
e4b6da |
my $t = ($colsep and
|
|
Packit |
e4b6da |
($row->ext->{current_colnum} !=
|
|
Packit |
e4b6da |
$tgroup->ext->{total_cols})) ? ' |' : '';
|
|
Packit |
e4b6da |
$row->ext->{this_format_line}->[$row->ext->{current_colnum} - 1]
|
|
Packit |
e4b6da |
=~ s/( \|)?$/$t/;
|
|
Packit |
e4b6da |
}
|
|
Packit |
e4b6da |
|
|
Packit |
e4b6da |
my $relative_advance = 1;
|
|
Packit |
e4b6da |
if(defined $end_col) {
|
|
Packit |
e4b6da |
for(my $i = $start_col->{colnum}; $i < $end_col->{colnum}; $i++) {
|
|
Packit |
e4b6da |
$row->ext->{this_format_line}->[$i] = 's';
|
|
Packit |
e4b6da |
}
|
|
Packit |
e4b6da |
$relative_advance = $end_col->{colnum} - $start_col->{colnum} + 1;
|
|
Packit |
e4b6da |
}
|
|
Packit |
e4b6da |
$elem->ext->{relative_advance} = $relative_advance;
|
|
Packit |
e4b6da |
|
|
Packit |
e4b6da |
if($elem->attr('morerows')) {
|
|
Packit |
e4b6da |
if($elem->attr('morerows') !~ /^\d+$/) {
|
|
Packit |
e4b6da |
$templates->warn_location($elem, "invalid morerows value --- ignoring\n");
|
|
Packit |
e4b6da |
} else {
|
|
Packit |
e4b6da |
|
|
Packit |
e4b6da |
for(my $i = 0; $i < $relative_advance; $i++) {
|
|
Packit |
e4b6da |
my $c = $row->ext->{current_colnum} + $i;
|
|
Packit |
e4b6da |
$t_elem->ext->{current_vspans}->[$c - 1]
|
|
Packit |
e4b6da |
= $elem->attr('morerows') + 1;
|
|
Packit |
e4b6da |
$t_elem->ext->{vspan_template_format}->[$c - 1]
|
|
Packit |
e4b6da |
= '^';
|
|
Packit |
e4b6da |
$t_elem->ext->{vspan_template_format}->[$c - 1]
|
|
Packit |
e4b6da |
.= ' |'
|
|
Packit |
e4b6da |
if $row->ext->{this_format_line}->[$row->ext->{current_colnum} - 1] =~ /\|$/;
|
|
Packit |
e4b6da |
}
|
|
Packit |
e4b6da |
}
|
|
Packit |
e4b6da |
}
|
|
Packit |
e4b6da |
|
|
Packit |
e4b6da |
if($elem->name eq 'entry') {
|
|
Packit |
e4b6da |
$self->{rw}->print("T{\n");
|
|
Packit |
e4b6da |
$templates->push_mode('');
|
|
Packit |
e4b6da |
} else {
|
|
Packit |
e4b6da |
$self->{rw}->print("\\&";;
|
|
Packit |
e4b6da |
$templates->push_mode('single-line-mode');
|
|
Packit |
e4b6da |
}
|
|
Packit |
e4b6da |
}
|
|
Packit |
e4b6da |
|
|
Packit |
e4b6da |
sub tbl_entry_end_handler
|
|
Packit |
e4b6da |
{
|
|
Packit |
e4b6da |
my ($self, $elem, $templates) = @_;
|
|
Packit |
e4b6da |
my $row = $elem->parent;
|
|
Packit |
e4b6da |
|
|
Packit |
e4b6da |
$templates->pop_mode();
|
|
Packit |
e4b6da |
if($elem->name eq 'entry') {
|
|
Packit |
e4b6da |
$self->{rw}->print_ws("\nT}");
|
|
Packit |
e4b6da |
}
|
|
Packit |
e4b6da |
|
|
Packit |
e4b6da |
tbl_advance_column($row, $self->{rw}, 0,
|
|
Packit |
e4b6da |
$elem->ext->{relative_advance});
|
|
Packit |
e4b6da |
}
|
|
Packit |
e4b6da |
|
|
Packit |
e4b6da |
$templates->add_rule('simentry<', 'table-mode', \&tbl_entry_start_handler);
|
|
Packit |
e4b6da |
$templates->add_rule('simentry>', 'table-mode', \&tbl_entry_end_handler);
|
|
Packit |
e4b6da |
$templates->add_rule('entry<', 'table-mode', \&tbl_entry_start_handler);
|
|
Packit |
e4b6da |
$templates->add_rule('entry>', 'table-mode', \&tbl_entry_end_handler);
|
|
Packit |
e4b6da |
|
|
Packit |
e4b6da |
$templates->add_rule('*<', 'table-mode', \&illegal_element_handler);
|
|
Packit |
e4b6da |
$templates->add_rule('text()', 'table-mode', \&illegal_text_handler);
|
|
Packit |
e4b6da |
|
|
Packit |
e4b6da |
#
|
|
Packit |
e4b6da |
# Minimal entrytbl support (from Craig Ruff)
|
|
Packit |
e4b6da |
#
|
|
Packit |
e4b6da |
$templates->add_rule('entrytbl<', 'table-mode', sub {
|
|
Packit |
e4b6da |
my ($self, $elem, $templates) = @_;
|
|
Packit |
e4b6da |
|
|
Packit |
e4b6da |
$elem->ext->{relative_advance} = 1;
|
|
Packit |
e4b6da |
if($elem->name eq 'entrytbl') {
|
|
Packit |
e4b6da |
$self->{rw}->print("T{\n");
|
|
Packit |
e4b6da |
$templates->push_mode('entrytbl-mode');
|
|
Packit |
e4b6da |
}
|
|
Packit |
e4b6da |
});
|
|
Packit |
e4b6da |
|
|
Packit |
e4b6da |
$templates->add_rule('entrytbl>', 'table-mode', sub {
|
|
Packit |
e4b6da |
my ($self, $elem, $templates) = @_;
|
|
Packit |
e4b6da |
my $row = $elem->parent;
|
|
Packit |
e4b6da |
|
|
Packit |
e4b6da |
$templates->pop_mode();
|
|
Packit |
e4b6da |
if($elem->name eq 'entrytbl') {
|
|
Packit |
e4b6da |
$self->{rw}->print_ws("\nT}");
|
|
Packit |
e4b6da |
}
|
|
Packit |
e4b6da |
|
|
Packit |
e4b6da |
tbl_advance_column($row, $self->{rw}, 0,
|
|
Packit |
e4b6da |
$elem->ext->{relative_advance});
|
|
Packit |
e4b6da |
});
|
|
Packit |
e4b6da |
|
|
Packit |
e4b6da |
$templates->add_rule('TP<', 'entrytbl-mode', sub {
|
|
Packit |
e4b6da |
my ($self, $elem, $templates) = @_;
|
|
Packit |
e4b6da |
$elem->parent->ext->{lastchild} = 'hanging';
|
|
Packit |
e4b6da |
});
|
|
Packit |
e4b6da |
|
|
Packit |
e4b6da |
$templates->add_rule('TP>', 'entrytbl-mode', sub {});
|
|
Packit |
e4b6da |
|
|
Packit |
e4b6da |
$templates->add_rule('TPtag<', 'entrytbl-mode', sub {
|
|
Packit |
e4b6da |
my ($self, $elem, $templates) = @_;
|
|
Packit |
e4b6da |
$self->{rw}->request('TP', $elem->parent->attr('indent'));
|
|
Packit |
e4b6da |
$templates->push_mode('single-line-mode');
|
|
Packit |
e4b6da |
});
|
|
Packit |
e4b6da |
|
|
Packit |
e4b6da |
$templates->add_rule('TPtag>', 'entrytbl-mode', sub {
|
|
Packit |
e4b6da |
my ($self, $elem, $templates) = @_;
|
|
Packit |
e4b6da |
$templates->pop_mode();
|
|
Packit |
e4b6da |
$self->{rw}->print_ws("\n");
|
|
Packit |
e4b6da |
});
|
|
Packit |
e4b6da |
|
|
Packit |
e4b6da |
$templates->add_rule('TPitem<', 'entrytbl-mode', sub {
|
|
Packit |
e4b6da |
my ($self, $elem, $templates) = @_;
|
|
Packit |
e4b6da |
$templates->push_mode('');
|
|
Packit |
e4b6da |
});
|
|
Packit |
e4b6da |
|
|
Packit |
e4b6da |
$templates->add_rule('TPitem>', 'entrytbl-mode', sub {
|
|
Packit |
e4b6da |
my ($self, $elem, $templates) = @_;
|
|
Packit |
e4b6da |
$templates->pop_mode();
|
|
Packit |
e4b6da |
});
|
|
Packit |
e4b6da |
|
|
Packit |
e4b6da |
$templates->add_rule('text()', 'entrytbl-mode', sub {
|
|
Packit |
e4b6da |
my ($self, $node, $templates) = @_;
|
|
Packit |
e4b6da |
|
|
Packit |
e4b6da |
mixed_inline_start($self, $node);
|
|
Packit |
e4b6da |
$self->{rw}->print_ws(
|
|
Packit |
e4b6da |
disambiguate_hyphen_minus(man_escape($node->{Data}), $node));
|
|
Packit |
e4b6da |
});
|
|
Packit |
e4b6da |
|
|
Packit |
e4b6da |
|
|
Packit |
e4b6da |
|
|
Packit |
e4b6da |
|
|
Packit |
e4b6da |
##################################################
|
|
Packit |
e4b6da |
#
|
|
Packit |
e4b6da |
# Character data
|
|
Packit |
e4b6da |
#
|
|
Packit |
e4b6da |
##################################################
|
|
Packit |
e4b6da |
|
|
Packit |
e4b6da |
sub save_text_handler
|
|
Packit |
e4b6da |
{
|
|
Packit |
e4b6da |
my ($self, $node, $templates) = @_;
|
|
Packit |
e4b6da |
$self->{output_save} .= $node->{Data};
|
|
Packit |
e4b6da |
}
|
|
Packit |
e4b6da |
|
|
Packit |
e4b6da |
sub single_line_text_handler {
|
|
Packit |
e4b6da |
my ($self, $node, $templates) = @_;
|
|
Packit |
e4b6da |
my $s = $node->{Data};
|
|
Packit |
e4b6da |
|
|
Packit |
e4b6da |
$s =~ tr/\n/ /;
|
|
Packit |
e4b6da |
$s = disambiguate_hyphen_minus(man_escape($s), $node);
|
|
Packit |
e4b6da |
|
|
Packit |
e4b6da |
mixed_inline_start($self, $node);
|
|
Packit |
e4b6da |
|
|
Packit |
e4b6da |
$self->{rw}->print_ws($s);
|
|
Packit |
e4b6da |
}
|
|
Packit |
e4b6da |
|
|
Packit |
e4b6da |
sub illegal_text_handler {
|
|
Packit |
e4b6da |
my ($self, $node, $templates) = @_;
|
|
Packit |
e4b6da |
|
|
Packit |
e4b6da |
if($node->{Data} =~ /[^ \t\r\n]/) {
|
|
Packit |
e4b6da |
$templates->warn_location($node, "character data is not allowed here");
|
|
Packit |
e4b6da |
}
|
|
Packit |
e4b6da |
}
|
|
Packit |
e4b6da |
|
|
Packit |
e4b6da |
$templates->add_rule('*<', 'single-line-mode', \&illegal_element_handler);
|
|
Packit |
e4b6da |
$templates->add_rule('text()', 'single-line-mode',
|
|
Packit |
e4b6da |
\&single_line_text_handler);
|
|
Packit |
e4b6da |
|
|
Packit |
e4b6da |
$templates->add_rule('*<', 'verbatim-mode', \&illegal_element_handler);
|
|
Packit |
e4b6da |
$templates->add_rule('text()', 'verbatim-mode', sub {
|
|
Packit |
e4b6da |
my ($self, $node, $templates) = @_;
|
|
Packit |
e4b6da |
$self->{rw}->print(
|
|
Packit |
e4b6da |
disambiguate_hyphen_minus(man_escape($node->{Data})));
|
|
Packit |
e4b6da |
});
|
|
Packit |
e4b6da |
|
|
Packit |
e4b6da |
$templates->add_rule('text()', sub {
|
|
Packit |
e4b6da |
my ($self, $node, $templates) = @_;
|
|
Packit |
e4b6da |
|
|
Packit |
e4b6da |
mixed_inline_start($self, $node);
|
|
Packit |
e4b6da |
|
|
Packit |
e4b6da |
my $text = man_escape($node->{Data});
|
|
Packit |
e4b6da |
$text = disambiguate_hyphen_minus($text, $node);
|
|
Packit |
e4b6da |
$text = disable_hyphenation($text, $node);
|
|
Packit |
e4b6da |
|
|
Packit |
e4b6da |
$self->{rw}->print_ws($text, $node);
|
|
Packit |
e4b6da |
});
|
|
Packit |
e4b6da |
|
|
Packit |
e4b6da |
|
|
Packit |
e4b6da |
|
|
Packit |
e4b6da |
|
|
Packit |
e4b6da |
##################################################
|
|
Packit |
e4b6da |
#
|
|
Packit |
e4b6da |
# Comments
|
|
Packit |
e4b6da |
#
|
|
Packit |
e4b6da |
##################################################
|
|
Packit |
e4b6da |
|
|
Packit |
e4b6da |
$templates->add_rule('comment<', sub {
|
|
Packit |
e4b6da |
my ($self, $elem, $templates) = @_;
|
|
Packit |
e4b6da |
$templates->push_mode('comment-mode');
|
|
Packit |
e4b6da |
});
|
|
Packit |
e4b6da |
|
|
Packit |
e4b6da |
$templates->add_rule('comment>', sub {
|
|
Packit |
e4b6da |
my ($self, $elem, $templates) = @_;
|
|
Packit |
e4b6da |
$templates->pop_mode('comment-mode');
|
|
Packit |
e4b6da |
$self->{rw}->comment($elem->ext->{content});
|
|
Packit |
e4b6da |
});
|
|
Packit |
e4b6da |
|
|
Packit |
e4b6da |
$templates->add_rule('*<', 'comment-mode', \&illegal_element_handler);
|
|
Packit |
e4b6da |
$templates->add_rule('text()', 'comment-mode', sub {
|
|
Packit |
e4b6da |
my ($self, $node, $templates) = @_;
|
|
Packit |
e4b6da |
$node->parent->ext->{content} .= $node->{Data};
|
|
Packit |
e4b6da |
});
|
|
Packit |
e4b6da |
|
|
Packit |
e4b6da |
|
|
Packit |
e4b6da |
|
|
Packit |
e4b6da |
|
|
Packit |
e4b6da |
##################################################
|
|
Packit |
e4b6da |
#
|
|
Packit |
e4b6da |
# Processing instructions
|
|
Packit |
e4b6da |
#
|
|
Packit |
e4b6da |
##################################################
|
|
Packit |
e4b6da |
|
|
Packit |
e4b6da |
$templates->add_rule('processing-instruction()', sub {
|
|
Packit |
e4b6da |
my ($self, $node, $templates) = @_;
|
|
Packit |
e4b6da |
|
|
Packit |
e4b6da |
if($node->{Target} eq 'man') {
|
|
Packit |
e4b6da |
my $data = $node->{Data};
|
|
Packit |
e4b6da |
$data =~ s/\
/\n/g;
|
|
Packit |
e4b6da |
$data =~ s/\
/\n/g;
|
|
Packit |
e4b6da |
$self->{rw}->print_ws($data);
|
|
Packit |
e4b6da |
}
|
|
Packit |
e4b6da |
});
|
|
Packit |
e4b6da |
|
|
Packit |
e4b6da |
$templates->add_rule('*<', \&illegal_element_handler);
|
|
Packit |
e4b6da |
sub illegal_element_handler {
|
|
Packit |
e4b6da |
my ($self, $node, $templates) = @_;
|
|
Packit |
e4b6da |
$templates->warn_location($node, "element not allowed here\n");
|
|
Packit |
e4b6da |
};
|
|
Packit |
e4b6da |
|
|
Packit |
e4b6da |
|
|
Packit |
e4b6da |
|
|
Packit |
e4b6da |
|
|
Packit |
e4b6da |
|
|
Packit |
e4b6da |
##################################################
|
|
Packit |
e4b6da |
#
|
|
Packit |
e4b6da |
# Main
|
|
Packit |
e4b6da |
#
|
|
Packit |
e4b6da |
##################################################
|
|
Packit |
e4b6da |
|
|
Packit |
e4b6da |
package main;
|
|
Packit |
e4b6da |
|
|
Packit |
e4b6da |
use XML::SAX::ParserFactory;
|
|
Packit |
e4b6da |
|
|
Packit |
e4b6da |
unshift(@ARGV, '-') unless @ARGV;
|
|
Packit |
e4b6da |
my $parser = XML::SAX::ParserFactory->parser(DocumentHandler => $manxml::templates);
|
|
Packit |
e4b6da |
|
|
Packit |
e4b6da |
foreach my $file (@ARGV)
|
|
Packit |
e4b6da |
{
|
|
Packit |
e4b6da |
if($file eq '-') {
|
|
Packit |
e4b6da |
$parser->parse_file(\*STDIN);
|
|
Packit |
e4b6da |
} else {
|
|
Packit |
e4b6da |
$parser->parse_uri($file);
|
|
Packit |
e4b6da |
}
|
|
Packit |
e4b6da |
}
|
|
Packit |
e4b6da |
|