1230 lines
27 KiB
Prolog
1230 lines
27 KiB
Prolog
=head1 NAME
|
|
|
|
docbook2man-spec - convert DocBook RefEntries to Unix manpages
|
|
|
|
=head1 SYNOPSIS
|
|
|
|
The SGMLSpm package from CPAN. This contains the sgmlspl script which
|
|
is used to grok this file. Use it like this:
|
|
|
|
nsgmls some-docbook-document.sgml | sgmlspl docbook2man-spec.pl
|
|
|
|
=head1 DESCRIPTION
|
|
|
|
This is a sgmlspl spec file that produces Unix-style
|
|
manpages from RefEntry markup.
|
|
|
|
See the accompanying RefEntry man page for 'plain new' documentation. :)
|
|
|
|
=head1 LIMITATIONS
|
|
|
|
Trying docbook2man on non-DocBook or non-conformant SGML results in
|
|
undefined behavior. :-)
|
|
|
|
This program is a slow, dodgy Perl script.
|
|
|
|
This program does not come close to supporting all the possible markup
|
|
in DocBook, and will produce wrong output in some cases with supported
|
|
markup.
|
|
|
|
=head1 TODO
|
|
|
|
Add new element handling and fix existing handling. Be robust.
|
|
Produce cleanest, readable man output as possible (unlike some
|
|
other converters). Follow Linux man(7) convention.
|
|
If this results in added logic in this script,
|
|
that's okay. The code should still be reasonably organized.
|
|
|
|
Make it faster. If Perl sucks port it to another language.
|
|
|
|
=head1 COPYRIGHT
|
|
|
|
Copyright (C) 1998-1999 Steve Cheng <steve@ggi-project.org>
|
|
|
|
This program is free software; you can redistribute it and/or modify it
|
|
under the terms of the GNU General Public License as published by the Free
|
|
Software Foundation; either version 2, or (at your option) any later
|
|
version.
|
|
|
|
You should have received a copy of the GNU General Public License along with
|
|
this program; see the file COPYING. If not, please write to the Free
|
|
Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
|
|
|
|
=cut
|
|
|
|
# $Id: docbook2man-spec.pl,v 1.4 2006/07/18 14:49:14 david__schmidt Exp $
|
|
|
|
use SGMLS; # Use the SGMLS package.
|
|
use SGMLS::Output; # Use stack-based output.
|
|
use SGMLS::Refs;
|
|
|
|
########################################################################
|
|
# SGMLSPL script produced automatically by the script sgmlspl.pl
|
|
#
|
|
# Document Type: any, but processes only RefEntries
|
|
# Edited by: me :)
|
|
########################################################################
|
|
|
|
$write_manpages = 0;
|
|
$blank_xrefs = 0;
|
|
|
|
sgml('start', sub {
|
|
push_output('nul');
|
|
$raw_cdata = 1; # Makes it a bit faster.
|
|
|
|
# Links file
|
|
open(LINKSFILE, ">manpage.links");
|
|
|
|
$Refs = new SGMLS::Refs("manpage.refs");
|
|
});
|
|
sgml('end', sub {
|
|
close(LINKSFILE);
|
|
if($blank_xrefs) {
|
|
print STDERR "Warning: output contains unresolved XRefs\n";
|
|
}
|
|
});
|
|
|
|
|
|
|
|
|
|
########################################################################
|
|
#
|
|
# Output helpers
|
|
#
|
|
########################################################################
|
|
|
|
# Our own version of sgml() and output() to allow simple string output
|
|
# to play well with roff's stupid whitespace rules.
|
|
|
|
sub man_sgml
|
|
{
|
|
if(ref($_[1]) eq 'CODE') {
|
|
return &sgml;
|
|
}
|
|
|
|
my $s = $_[1];
|
|
|
|
$s =~ s/\\/\\\\/g;
|
|
$s =~ s/'/\\'/g;
|
|
|
|
# \n at the beginning means start at beginning of line
|
|
if($s =~ s/^\n//) {
|
|
$sub = 'sub { output "\n" unless $newline_last++; ';
|
|
if($s eq '') {
|
|
sgml($_[0], eval('sub { output "\n" unless $newline_last++; }'));
|
|
} elsif($s =~ /\n$/) {
|
|
sgml($_[0], eval("sub { output \"\\n\" unless \$newline_last++; output '$s'; }"));
|
|
} else {
|
|
sgml($_[0], eval("sub { output \"\\n\" unless \$newline_last; output '$s'; \$newline_last = 0; }"));
|
|
}
|
|
} else {
|
|
if($s =~ /\n$/) {
|
|
sgml($_[0], eval("sub { output '$s'; \$newline_last = 1; }"));
|
|
} else {
|
|
sgml($_[0], eval("sub { output '$s'; \$newline_last = 0; }"));
|
|
}
|
|
}
|
|
}
|
|
|
|
sub man_output
|
|
{
|
|
$_ = shift;
|
|
if(s/^\n//) {
|
|
output "\n" unless $newline_last++;
|
|
}
|
|
return if $_ eq '';
|
|
|
|
output $_;
|
|
|
|
if(@_) {
|
|
output @_;
|
|
$newline_last = (pop(@_) =~ /\n$/);
|
|
} else {
|
|
$newline_last = ($_ =~ /\n$/)
|
|
}
|
|
}
|
|
|
|
# Fold lines into one, quote some characters
|
|
sub fold_string
|
|
{
|
|
$_ = shift;
|
|
|
|
s/\\/\\\\/g;
|
|
s/"/\\\&"/g;
|
|
|
|
# Change tabs to spaces
|
|
tr/\t\n/ /;
|
|
|
|
# Trim whitespace from beginning and end.
|
|
s/^ +//;
|
|
s/ +$//;
|
|
|
|
return $_;
|
|
}
|
|
|
|
sub save_cdata()
|
|
{
|
|
$raw_cdata++;
|
|
push_output('string');
|
|
}
|
|
|
|
sub bold_on()
|
|
{
|
|
# If the last font is also bold, don't change anything.
|
|
# Basically this is to just get more readable man output.
|
|
if($fontstack[$#fontstack] ne 'bold') {
|
|
if(!$raw_cdata) {
|
|
output '\fB';
|
|
$newline_last = 0;
|
|
}
|
|
}
|
|
push(@fontstack, 'bold');
|
|
}
|
|
|
|
sub italic_on()
|
|
{
|
|
# If the last font is also italic, don't change anything.
|
|
if($fontstack[$#fontstack] ne 'italic') {
|
|
if(!$raw_cdata) {
|
|
output '\fI';
|
|
$newline_last = 0;
|
|
}
|
|
}
|
|
push(@fontstack, 'italic');
|
|
}
|
|
|
|
sub font_off()
|
|
{
|
|
my $thisfont = pop(@fontstack);
|
|
my $lastfont = $fontstack[$#fontstack];
|
|
|
|
# Only output font change if it is different
|
|
if($thisfont ne $lastfont) {
|
|
if($raw_cdata) { return; }
|
|
elsif($lastfont eq 'bold') { output '\fB'; }
|
|
elsif($lastfont eq 'italic') { output '\fI'; }
|
|
else { output '\fR'; }
|
|
|
|
$newline_last = 0;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
########################################################################
|
|
#
|
|
# Manpage management
|
|
#
|
|
########################################################################
|
|
|
|
sgml('<REFENTRY>', sub {
|
|
# This will be overwritten at end of REFMETA, when we know the name of the page.
|
|
pop_output();
|
|
|
|
$write_manpages = 1; # Currently writing manpage.
|
|
|
|
$nocollapse_whitespace = 0; # Current whitespace collapse counter.
|
|
$newline_last = 1; # At beginning of line?
|
|
# Just a bit of warning, you will see this variable manipulated
|
|
# manually a lot. It makes the code harder to follow but it
|
|
# saves you from having to worry about collapsing at the end of
|
|
# parse, stopping at verbatims, etc.
|
|
$raw_cdata = 0; # Instructs certain output functions to
|
|
# leave CDATA alone, so we can assign
|
|
# it to a string and process it, etc.
|
|
@fontstack = (); # Fonts being activated.
|
|
|
|
$manpage_title = ''; # Needed for indexing.
|
|
$manpage_sect = '';
|
|
@manpage_names = ();
|
|
|
|
$manpage_misc = '';
|
|
|
|
$list_nestlevel = 0; # Indent certain nested content.
|
|
});
|
|
sgml('</REFENTRY>', sub {
|
|
if(!$newline_last) {
|
|
output "\n";
|
|
}
|
|
|
|
$write_manpages = 0;
|
|
$raw_cdata = 1;
|
|
push_output('nul');
|
|
});
|
|
|
|
sgml('</REFMETA>', sub {
|
|
push_output('file', "$manpage_title.$manpage_sect");
|
|
|
|
output <<_END_BANNER;
|
|
.\\" This manpage has been automatically generated by docbook2man
|
|
.\\" from a DocBook document. This tool can be found at:
|
|
.\\" <http://shell.ipoline.com/~elmert/comp/docbook2X/>
|
|
.\\" Please send any bug reports, improvements, comments, patches,
|
|
.\\" etc. to Steve Cheng <steve\@ggi-project.org>.
|
|
_END_BANNER
|
|
|
|
my $manpage_date = `date "+%d %B %Y"`;
|
|
|
|
output '.TH "';
|
|
|
|
# If the title is not mixed-case, convention says to
|
|
# uppercase the whole title. (The canonical title is
|
|
# lowercase.)
|
|
if($manpage_title =~ /[A-Z]/) {
|
|
output fold_string($manpage_title);
|
|
} else {
|
|
output uc(fold_string($manpage_title));
|
|
}
|
|
|
|
output '" "', fold_string($manpage_sect),
|
|
'" "', fold_string(`date "+%d %B %Y"`),
|
|
'" "', $manpage_misc,
|
|
'" "', $manpage_manual,
|
|
"\"\n";
|
|
|
|
$newline_last = 1;
|
|
|
|
# References to this RefEntry.
|
|
my $id = $_[0]->parent->attribute('ID')->value;
|
|
if($id ne '') {
|
|
# The 'package name' part of the section should
|
|
# not be used when citing it.
|
|
my ($sectnum) = ($manpage_sect =~ /([0-9]*)/);
|
|
|
|
if($_[0]->parent->attribute('XREFLABEL')->value eq '') {
|
|
$Refs->put("refentry:$id", "$manpage_title($sectnum)");
|
|
} else {
|
|
$Refs->put("refentry:$id",
|
|
$_[0]->parent->attribute('XREFLABEL')->value .
|
|
"($sectnum)");
|
|
}
|
|
}
|
|
});
|
|
|
|
sgml('<REFENTRYTITLE>', sub {
|
|
if($_[0]->in('REFMETA')) {
|
|
save_cdata();
|
|
} else {
|
|
# Manpage citations are in bold.
|
|
bold_on();
|
|
}
|
|
});
|
|
sgml('</REFENTRYTITLE>', sub {
|
|
if($_[0]->in('REFMETA')) {
|
|
$raw_cdata--;
|
|
$manpage_title = pop_output();
|
|
}
|
|
else { font_off(); }
|
|
});
|
|
|
|
sgml('<MANVOLNUM>', sub {
|
|
if($_[0]->in('REFMETA')) {
|
|
save_cdata();
|
|
} else {
|
|
# Manpage citations use ().
|
|
output '(';
|
|
}
|
|
});
|
|
sgml('</MANVOLNUM>', sub {
|
|
if($_[0]->in('REFMETA')) {
|
|
$raw_cdata--;
|
|
$manpage_sect = pop_output();
|
|
}
|
|
else { output ')' }
|
|
});
|
|
|
|
sgml('<REFMISCINFO>', \&save_cdata);
|
|
sgml('</REFMISCINFO>', sub {
|
|
$raw_cdata--;
|
|
$manpage_misc = fold_string(pop_output());
|
|
});
|
|
|
|
|
|
# NAME section
|
|
man_sgml('<REFNAMEDIV>', "\n.SH NAME\n");
|
|
|
|
sgml('<REFNAME>', \&save_cdata);
|
|
sgml('</REFNAME>', sub {
|
|
$raw_cdata--;
|
|
push(@manpage_names, pop_output());
|
|
});
|
|
|
|
sgml('<REFPURPOSE>', \&save_cdata);
|
|
sgml('</REFPURPOSE>', sub {
|
|
$raw_cdata--;
|
|
my $manpage_purpose = fold_string(pop_output());
|
|
|
|
for(my $i = 0; $i < $#manpage_names; $i++) {
|
|
output fold_string($manpage_names[$i]), ', ';
|
|
}
|
|
|
|
output fold_string($manpage_names[$#manpage_names]);
|
|
output " \\- $manpage_purpose\n";
|
|
|
|
$newline_last = 1;
|
|
|
|
foreach(@manpage_names) {
|
|
# Don't link to itself
|
|
if($_ ne $manpage_title) {
|
|
print LINKSFILE "$manpage_title.$manpage_sect $_.$manpage_sect\n";
|
|
}
|
|
}
|
|
});
|
|
|
|
man_sgml('<REFCLASS>', "\n.sp\n");
|
|
|
|
#RefDescriptor
|
|
|
|
|
|
|
|
|
|
|
|
########################################################################
|
|
#
|
|
# SYNOPSIS section and synopses
|
|
#
|
|
########################################################################
|
|
|
|
man_sgml('<REFSYNOPSISDIV>', "\n.SH SYNOPSIS\n");
|
|
man_sgml('</REFSYNOPSISDIV>', "\n");
|
|
|
|
## FIXME! Must be made into block elements!!
|
|
#sgml('<FUNCSYNOPSIS>', \&bold_on);
|
|
#sgml('</FUNCSYNOPSIS>', \&font_off);
|
|
#sgml('<CMDSYNOPSIS>', \&bold_on);
|
|
#sgml('</CMDSYNOPSIS>', \&font_off);
|
|
|
|
man_sgml('<FUNCSYNOPSIS>', sub {
|
|
man_output("\n.sp\n");
|
|
bold_on();
|
|
});
|
|
man_sgml('</FUNCSYNOPSIS>', sub {
|
|
font_off();
|
|
man_output("\n");
|
|
});
|
|
|
|
man_sgml('<CMDSYNOPSIS>', "\n\n");
|
|
man_sgml('</CMDSYNOPSIS>', "\n\n");
|
|
|
|
man_sgml('<FUNCPROTOTYPE>', "\n.sp\n");
|
|
|
|
# Arguments to functions. This is C convention.
|
|
sub paramdef
|
|
{
|
|
if($_[0]->parent->ext->{'inparams'}) {
|
|
output ', ';
|
|
} else {
|
|
output ' (';
|
|
$_[0]->parent->ext->{'inparams'} = 1;
|
|
}
|
|
}
|
|
man_sgml('<PARAMDEF>', \¶mdef);
|
|
man_sgml('</FUNCPROTOTYPE>', ");\n");
|
|
man_sgml('<VOID>', "(void");
|
|
man_sgml('<VARARGS>', "(...");
|
|
|
|
|
|
|
|
sub group_start
|
|
{
|
|
if(not $_[0]->parent->in('TERM')) {
|
|
if($_[0]->attribute('CHOICE')->value =~ /opt/i) {
|
|
output ' [';
|
|
} elsif($_[0]->attribute('CHOICE')->value =~ /req/i) {
|
|
output ' {';
|
|
}
|
|
}
|
|
$_[0]->ext->{'count'} = 1;
|
|
}
|
|
sub group_end
|
|
{
|
|
if($_[0]->attribute('REP')->value =~ /^Repeat/i) {
|
|
italic_on();
|
|
output ' ...';
|
|
font_off();
|
|
}
|
|
if(not $_[0]->parent->in('TERM')) {
|
|
if($_[0]->attribute('CHOICE')->value =~ /opt/i) {
|
|
output ' ]';
|
|
} elsif($_[0]->attribute('CHOICE')->value =~ /req/i) {
|
|
output ' }';
|
|
}
|
|
}
|
|
}
|
|
|
|
sub arg_start
|
|
{
|
|
# my $choice = $_[0]->attribute('CHOICE')->value;
|
|
|
|
# The content model for CmdSynopsis doesn't include #PCDATA,
|
|
# so we won't see any of the whitespace in the source file,
|
|
# so we have to add it after each component.
|
|
output ' ';
|
|
|
|
if($_[0]->in('GROUP')) {
|
|
output '| ' if $_[0]->parent->ext->{'count'} > 1;
|
|
$_[0]->parent->ext->{'count'}++;
|
|
} elsif($_[0]->attribute('CHOICE')->value =~ /opt/i) {
|
|
output '[ ';
|
|
}
|
|
bold_on();
|
|
}
|
|
sub arg_end
|
|
{
|
|
font_off();
|
|
if($_[0]->attribute('REP')->value =~ /^Repeat/i) {
|
|
italic_on();
|
|
output ' ...';
|
|
font_off();
|
|
}
|
|
if($_[0]->attribute('CHOICE')->value =~ /opt/i and
|
|
not $_[0]->in('GROUP')) {
|
|
output ' ]';
|
|
}
|
|
}
|
|
|
|
sgml('<ARG>', \&arg_start);
|
|
sgml('</ARG>', \&arg_end);
|
|
sgml('<GROUP>', \&group_start);
|
|
sgml('</GROUP>', \&group_end);
|
|
|
|
sgml('<OPTION>', \&bold_on);
|
|
sgml('</OPTION>', \&font_off);
|
|
|
|
man_sgml('<SBR>', "\n ");
|
|
|
|
|
|
########################################################################
|
|
#
|
|
# General sections
|
|
#
|
|
########################################################################
|
|
|
|
# The name of the section is handled by TITLE. This just sets
|
|
# up the roff markup.
|
|
man_sgml('<REFSECT1>', "\n.SH ");
|
|
man_sgml('<REFSECT2>', "\n.SS ");
|
|
man_sgml('<REFSECT3>', "\n.SS ");
|
|
|
|
|
|
########################################################################
|
|
#
|
|
# Titles, metadata.
|
|
#
|
|
########################################################################
|
|
|
|
sgml('<TITLE>', sub {
|
|
if($_[0]->in('REFERENCE') or $_[0]->in('BOOK')) {
|
|
$write_manpages = 1;
|
|
}
|
|
save_cdata();
|
|
});
|
|
sgml('</TITLE>', sub {
|
|
my $title = fold_string(pop_output());
|
|
$raw_cdata--;
|
|
|
|
if($_[0]->in('REFERENCE') or $_[0]->in('BOOK')) {
|
|
# We use TITLE of enclosing Reference or Book as manual name
|
|
$manpage_manual = $title;
|
|
$write_manpages = 0;
|
|
}
|
|
elsif(exists $_[0]->parent->ext->{'title'}) {
|
|
# By far the easiest case. Just fold the string as
|
|
# above, and then set the parent element's variable.
|
|
$_[0]->parent->ext->{'title'} = $title;
|
|
}
|
|
else {
|
|
# If the parent element's handlers are lazy,
|
|
# output the folded string for them :)
|
|
# We assume they want uppercase and a newline.
|
|
output '"', uc($title), "\"\n";
|
|
$newline_last = 1;
|
|
}
|
|
});
|
|
|
|
sgml('<ATTRIBUTION>', sub { push_output('string') });
|
|
sgml('</ATTRIBUTION>', sub { $_[0]->parent->ext->{'attribution'} = pop_output(); });
|
|
|
|
|
|
# IGNORE.
|
|
sgml('<DOCINFO>', sub { push_output('nul'); });
|
|
sgml('</DOCINFO>', sub { pop_output(); });
|
|
sgml('<REFSECT1INFO>', sub { push_output('nul'); });
|
|
sgml('</REFSECT1INFO>', sub { pop_output(); });
|
|
sgml('<REFSECT2INFO>', sub { push_output('nul'); });
|
|
sgml('</REFSECT2INFO>', sub { pop_output(); });
|
|
sgml('<REFSECT3INFO>', sub { push_output('nul'); });
|
|
sgml('</REFSECT3INFO>', sub { pop_output(); });
|
|
|
|
sgml('<INDEXTERM>', sub { push_output('nul'); });
|
|
sgml('</INDEXTERM>', sub { pop_output(); });
|
|
|
|
|
|
########################################################################
|
|
#
|
|
# Set bold on enclosed content
|
|
#
|
|
########################################################################
|
|
|
|
sgml('<APPLICATION>', \&bold_on); sgml('</APPLICATION>', \&font_off);
|
|
|
|
sgml('<CLASSNAME>', \&bold_on); sgml('</CLASSNAME>', \&font_off);
|
|
sgml('<STRUCTNANE>', \&bold_on); sgml('</STRUCTNAME>', \&font_off);
|
|
sgml('<STRUCTFIELD>', \&bold_on); sgml('</STRUCTFIELD>', \&font_off);
|
|
sgml('<SYMBOL>', \&bold_on); sgml('</SYMBOL>', \&font_off);
|
|
sgml('<TYPE>', \&bold_on); sgml('</TYPE>', \&font_off);
|
|
|
|
sgml('<ENVAR>', \&bold_on); sgml('</ENVAR>', \&font_off);
|
|
|
|
sgml('<FUNCTION>', \&bold_on); sgml('</FUNCTION>', \&font_off);
|
|
|
|
sgml('<EMPHASIS>', \&bold_on); sgml('</EMPHASIS>', \&font_off);
|
|
|
|
sgml('<ERRORNAME>', \&bold_on); sgml('</ERRORNAME>', \&font_off);
|
|
# ERRORTYPE
|
|
|
|
sgml('<COMMAND>', \&bold_on); sgml('</COMMAND>', \&font_off);
|
|
|
|
sgml('<GUIBUTTON>', \&bold_on); sgml('</GUIBUTTON>', \&font_off);
|
|
sgml('<GUIICON>', \&bold_on); sgml('</GUIICON>', \&font_off);
|
|
# GUILABEL
|
|
# GUIMENU
|
|
# GUIMENUITEM
|
|
# GUISUBMENU
|
|
# MENUCHOICE
|
|
# MOUSEBUTTON
|
|
|
|
sgml('<ACCEL>', \&bold_on); sgml('</ACCEL>', \&font_off);
|
|
sgml('<KEYCAP>', \&bold_on); sgml('</KEYCAP>', \&font_off);
|
|
sgml('<KEYSYM>', \&bold_on); sgml('</KEYSYM>', \&font_off);
|
|
# KEYCODE
|
|
# KEYCOMBO
|
|
# SHORTCUT
|
|
|
|
sgml('<USERINPUT>', \&bold_on); sgml('</USERINPUT>', \&font_off);
|
|
|
|
sgml('<INTERFACEDEFINITION>', \&bold_on);
|
|
sgml('</INTERFACEDEFINITION>', \&font_off);
|
|
|
|
# May need to look at the CLASS
|
|
sgml('<SYSTEMITEM>', \&bold_on);
|
|
sgml('</SYSTEMITEM>', \&font_off);
|
|
|
|
|
|
|
|
|
|
|
|
########################################################################
|
|
#
|
|
# Set italic on enclosed content
|
|
#
|
|
########################################################################
|
|
|
|
sgml('<FIRSTTERM>', \&italic_on); sgml('</FIRSTTERM>', \&font_off);
|
|
|
|
sgml('<FILENAME>', \&italic_on); sgml('</FILENAME>', \&font_off);
|
|
sgml('<PARAMETER>', \&italic_on); sgml('</PARAMETER>', \&font_off);
|
|
sgml('<PROPERTY>', \&italic_on); sgml('</PROPERTY>', \&font_off);
|
|
|
|
sgml('<REPLACEABLE>', sub {
|
|
italic_on();
|
|
if($_[0]->in('TOKEN')) {
|
|
# When tokenizing, follow more 'intuitive' convention
|
|
output "<";
|
|
}
|
|
});
|
|
sgml('</REPLACEABLE>', sub {
|
|
if($_[0]->in('TOKEN')) {
|
|
output ">";
|
|
}
|
|
font_off();
|
|
});
|
|
|
|
sgml('<CITETITLE>', \&italic_on); sgml('</CITETITLE>', \&font_off);
|
|
sgml('<FOREIGNPHRASE>', \&italic_on); sgml('</FOREIGNPHRASE>', \&font_off);
|
|
|
|
sgml('<LINEANNOTATION>', \&italic_on); sgml('</LINEANNOTATION>', \&font_off);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
########################################################################
|
|
#
|
|
# Other 'inline' elements
|
|
#
|
|
########################################################################
|
|
|
|
man_sgml('<EMAIL>', '<');
|
|
man_sgml('</EMAIL>', '>');
|
|
man_sgml('<OPTIONAL>', '[');
|
|
man_sgml('</OPTIONAL>', ']');
|
|
|
|
man_sgml('</TRADEMARK>', "\\u\\s-2TM\\s+2\\d");
|
|
|
|
man_sgml('<COMMENT>', "[Comment: ");
|
|
man_sgml('</COMMENT>', "]");
|
|
|
|
man_sgml('<QUOTE>', "``");
|
|
man_sgml('</QUOTE>', "''");
|
|
|
|
#man_sgml('<LITERAL>', '"');
|
|
#man_sgml('</LITERAL>', '"');
|
|
|
|
# No special presentation:
|
|
|
|
# AUTHOR
|
|
# AUTHORINITIALS
|
|
|
|
# ABBREV
|
|
# ACTION
|
|
# ACRONYM
|
|
# ALT
|
|
# CITATION
|
|
# PHRASE
|
|
# QUOTE
|
|
# WORDASWORD
|
|
|
|
# COMPUTEROUTPUT
|
|
# MARKUP
|
|
# PROMPT
|
|
# RETURNVALUE
|
|
# SGMLTAG
|
|
# TOKEN
|
|
|
|
# DATABASE
|
|
# HARDWARE
|
|
# INTERFACE
|
|
# MEDIALABEL
|
|
|
|
# There doesn't seem to be a good way to represent LITERAL in -man
|
|
|
|
|
|
|
|
########################################################################
|
|
#
|
|
# Paragraph and paragraph-like elements
|
|
#
|
|
########################################################################
|
|
|
|
sub para_start {
|
|
output "\n" unless $newline_last++;
|
|
|
|
# In lists, etc., don't start paragraph with .PP since
|
|
# the indentation will be gone.
|
|
|
|
if($_[0]->parent->ext->{'nobreak'}==1) {
|
|
# Usually this is the FIRST element of
|
|
# a hanging tag, so we MUST not do a full
|
|
# paragraph break.
|
|
$_[0]->parent->ext->{'nobreak'} = 2;
|
|
} elsif($_[0]->parent->ext->{'nobreak'}==2) {
|
|
# Usually these are the NEXT elements of
|
|
# a hanging tag. If we break using a blank
|
|
# line, we're okay.
|
|
output "\n";
|
|
} else {
|
|
# Normal case. (For indented blocks too, at least
|
|
# -man isn't so braindead in this area.)
|
|
output ".PP\n";
|
|
}
|
|
}
|
|
# Actually applies to a few other block elements as well
|
|
sub para_end {
|
|
output "\n" unless $newline_last++;
|
|
}
|
|
|
|
sgml('<PARA>', \¶_start);
|
|
sgml('</PARA>', \¶_end);
|
|
sgml('<SIMPARA>', \¶_start);
|
|
sgml('</SIMPARA>', \¶_end);
|
|
|
|
# Nothing special, except maybe FIXME set nobreak.
|
|
sgml('<INFORMALEXAMPLE>', \¶_start);
|
|
sgml('</INFORMALEXAMPLE>', \¶_end);
|
|
|
|
|
|
|
|
|
|
|
|
########################################################################
|
|
#
|
|
# Blocks using SS sections
|
|
#
|
|
########################################################################
|
|
|
|
# FIXME: We need to consider the effects of SS
|
|
# in a hanging tag :(
|
|
|
|
# Complete with the optional-title dilemma (again).
|
|
sgml('<ABSTRACT>', sub {
|
|
$_[0]->ext->{'title'} = 'ABSTRACT';
|
|
output "\n" unless $newline_last++;
|
|
push_output('string');
|
|
});
|
|
sgml('</ABSTRACT>', sub {
|
|
my $content = pop_output();
|
|
|
|
# As ABSTRACT is never on the same level as RefSect1,
|
|
# this leaves us with only .SS in terms of -man macros.
|
|
output ".SS \"", uc($_[0]->ext->{'title'}), "\"\n";
|
|
|
|
output $content;
|
|
output "\n" unless $newline_last++;
|
|
});
|
|
|
|
# Ah, I needed a break. Example always has a title.
|
|
man_sgml('<EXAMPLE>', "\n.SS ");
|
|
sgml('</EXAMPLE>', \¶_end);
|
|
|
|
# Same with sidebar.
|
|
man_sgml('<SIDEBAR>', "\n.SS ");
|
|
sgml('</SIDEBAR>', \¶_end);
|
|
|
|
# NO title.
|
|
man_sgml('<HIGHLIGHTS>', "\n.SS HIGHLIGHTS\n");
|
|
sgml('</HIGHLIGHTS>', \¶_end);
|
|
|
|
|
|
|
|
|
|
########################################################################
|
|
#
|
|
# Indented 'Block' elements
|
|
#
|
|
########################################################################
|
|
|
|
sub indent_block_start
|
|
{
|
|
output "\n" unless $newline_last++;
|
|
output ".sp\n.RS\n";
|
|
}
|
|
sub indent_block_end
|
|
{
|
|
output "\n" unless $newline_last++;
|
|
output ".RE\n";
|
|
}
|
|
|
|
# This element is almost like an admonition (below),
|
|
# only the default title is blank :)
|
|
|
|
sgml('<BLOCKQUOTE>', sub {
|
|
$_[0]->ext->{'title'} = '';
|
|
output "\n" unless $newline_last++;
|
|
push_output('string');
|
|
});
|
|
sgml('</BLOCKQUOTE>', sub {
|
|
my $content = pop_output();
|
|
|
|
indent_block_start();
|
|
|
|
if($_[0]->ext->{'title'}) {
|
|
output ".B \"", $_[0]->ext->{'title'}, ":\"\n";
|
|
}
|
|
|
|
output $content;
|
|
|
|
if($_[0]->ext->{'attribution'}) {
|
|
output "\n" unless $newline_last++;
|
|
# One place where roff's space-sensitivity makes sense :)
|
|
output "\n -- ";
|
|
output $_[0]->ext->{'attribution'} . "\n";
|
|
}
|
|
|
|
indent_block_end();
|
|
});
|
|
|
|
# Set off admonitions from the rest of the text by indenting.
|
|
# FIXME: Need to check if this works inside paragraphs, not enclosing them.
|
|
sub admonition_end {
|
|
my $content = pop_output();
|
|
|
|
indent_block_start();
|
|
|
|
# When the admonition is only one paragraph,
|
|
# it looks nicer if the title was inline.
|
|
my $num_para;
|
|
while ($content =~ /^\.PP/gm) { $num_para++ }
|
|
if($num_para==1) {
|
|
$content =~ s/^\.PP\n//;
|
|
}
|
|
|
|
output ".B \"" . $_[0]->ext->{'title'} . ":\"\n";
|
|
output $content;
|
|
|
|
indent_block_end();
|
|
}
|
|
|
|
sgml('<NOTE>', sub {
|
|
# We can't see right now whether or not there is a TITLE
|
|
# element, so we have to save the output now and add it back
|
|
# at the end of this admonition.
|
|
$_[0]->ext->{'title'} = 'Note';
|
|
|
|
# Although admonition_end's indent_block_start will do this,
|
|
# we need to synchronize the output _now_
|
|
output "\n" unless $newline_last++;
|
|
|
|
push_output('string');
|
|
});
|
|
sgml('</NOTE>', \&admonition_end);
|
|
|
|
# Same as above.
|
|
sgml('<WARNING>', sub {
|
|
$_[0]->ext->{'title'} = 'Warning';
|
|
output "\n" unless $newline_last++;
|
|
push_output('string');
|
|
});
|
|
sgml('</WARNING>', \&admonition_end);
|
|
|
|
sgml('<TIP>', sub {
|
|
$_[0]->ext->{'title'} = 'Tip';
|
|
output "\n" unless $newline_last++;
|
|
push_output('string');
|
|
});
|
|
sgml('</TIP>', \&admonition_end);
|
|
sgml('<CAUTION>', sub {
|
|
$_[0]->ext->{'title'} = 'Caution';
|
|
output "\n" unless $newline_last++;
|
|
push_output('string');
|
|
});
|
|
sgml('</CAUTION>', \&admonition_end);
|
|
|
|
sgml('<IMPORTANT>', sub {
|
|
$_[0]->ext->{'title'} = 'Important';
|
|
output "\n" unless $newline_last++;
|
|
push_output('string');
|
|
});
|
|
sgml('</IMPORTANT>', \&admonition_end);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
########################################################################
|
|
#
|
|
# Verbatim displays.
|
|
#
|
|
########################################################################
|
|
|
|
sub verbatim_start {
|
|
output "\n" unless $newline_last++;
|
|
|
|
if($_[0]->parent->ext->{'nobreak'}==1) {
|
|
# Usually this is the FIRST element of
|
|
# a hanging tag, so we MUST not do a full
|
|
# paragraph break.
|
|
$_[0]->parent->ext->{'nobreak'} = 2;
|
|
} else {
|
|
output "\n";
|
|
}
|
|
|
|
output(".nf\n") unless $nocollapse_whitespace++;
|
|
}
|
|
|
|
sub verbatim_end {
|
|
output "\n" unless $newline_last++;
|
|
output(".fi\n") unless --$nocollapse_whitespace;
|
|
}
|
|
|
|
sgml('<PROGRAMLISTING>', \&verbatim_start);
|
|
sgml('</PROGRAMLISTING>', \&verbatim_end);
|
|
|
|
sgml('<SCREEN>', \&verbatim_start);
|
|
sgml('</SCREEN>', \&verbatim_end);
|
|
|
|
sgml('<LITERALLAYOUT>', \&verbatim_start);
|
|
sgml('</LITERALLAYOUT>', \&verbatim_end);
|
|
|
|
#sgml('<SYNOPSIS>', sub {
|
|
# if($_[0]->attribute('FORMAT')->value =~ /linespecific/i) {
|
|
# &verbatim_start;
|
|
# } else {
|
|
# roffcmd("");
|
|
# }
|
|
#});
|
|
#
|
|
#sgml('</SYNOPSIS>', sub {
|
|
# if($_[0]->attribute('FORMAT')->value =~ /linespecific/i) {
|
|
# &verbatim_end;
|
|
# }
|
|
# else {
|
|
# roffcmd("");# not sure about this.
|
|
# }
|
|
#});
|
|
sgml('<SYNOPSIS>', \&verbatim_start);
|
|
sgml('</SYNOPSIS>', \&verbatim_end);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
########################################################################
|
|
#
|
|
# Lists
|
|
#
|
|
########################################################################
|
|
|
|
# Indent nested lists.
|
|
sub indent_list_start {
|
|
if($list_nestlevel++) {
|
|
output "\n" unless $newline_last++;
|
|
output ".RS\n";
|
|
}
|
|
}
|
|
sub indent_list_end {
|
|
if(--$list_nestlevel) {
|
|
output "\n" unless $newline_last++;
|
|
output ".RE\n";
|
|
}
|
|
}
|
|
|
|
sgml('<VARIABLELIST>', \&indent_list_start);
|
|
sgml('</VARIABLELIST>', \&indent_list_end);
|
|
sgml('<ITEMIZEDLIST>', \&indent_list_start);
|
|
sgml('</ITEMIZEDLIST>', \&indent_list_end);
|
|
sgml('<ORDEREDLIST>', sub {
|
|
indent_list_start();
|
|
$_[0]->ext->{'count'} = 1;
|
|
});
|
|
sgml('</ORDEREDLIST>', \&indent_list_end);
|
|
sgml('<GLOSSLIST>', \&indent_list_start);
|
|
sgml('</GLOSSLIST>', \&indent_list_end);
|
|
|
|
# Output content on one line, bolded.
|
|
sgml('<TERM>', sub {
|
|
output "\n" unless $newline_last++;
|
|
output ".TP\n";
|
|
bold_on();
|
|
push_output('string');
|
|
});
|
|
sgml('</TERM>', sub {
|
|
my $term = pop_output();
|
|
$term =~ tr/\n/ /;
|
|
output $term;
|
|
font_off();
|
|
output "\n";
|
|
$newline_last = 1;
|
|
});
|
|
sgml('<GLOSSTERM>', sub {
|
|
output "\n" unless $newline_last++;
|
|
output ".TP\n";
|
|
bold_on();
|
|
push_output('string');
|
|
});
|
|
sgml('</GLOSSTERM>', sub {
|
|
my $term = pop_output();
|
|
$term =~ tr/\n/ /;
|
|
output $term;
|
|
font_off();
|
|
output "\n";
|
|
$newline_last = 1;
|
|
});
|
|
|
|
sgml('<LISTITEM>', sub {
|
|
# A bulleted list.
|
|
if($_[0]->in('ITEMIZEDLIST')) {
|
|
output "\n" unless $newline_last++;
|
|
output ".TP 0.2i\n\\(bu\n";
|
|
}
|
|
|
|
# Need numbers.
|
|
# Assume Arabic numeration for now.
|
|
elsif($_[0]->in('ORDEREDLIST')) {
|
|
output "\n" unless $newline_last++;
|
|
output ".TP 3\n", $_[0]->parent->ext->{'count'}++, ". \n";
|
|
}
|
|
|
|
$_[0]->ext->{'nobreak'} = 1;
|
|
});
|
|
sgml('<GLOSSDEF>', sub {
|
|
$_[0]->ext->{'nobreak'} = 1;
|
|
});
|
|
|
|
sgml('<SIMPLELIST>', sub {
|
|
$_[0]->ext->{'first_member'} = 1;
|
|
});
|
|
|
|
sgml('<MEMBER>', sub {
|
|
my $parent = $_[0]->parent;
|
|
|
|
if($parent->attribute('TYPE')->value =~ /Inline/i) {
|
|
if($parent->ext->{'first_member'}) {
|
|
# If this is the first member don't put any commas
|
|
$parent->ext->{'first_member'} = 0;
|
|
} else {
|
|
output ", ";
|
|
}
|
|
} elsif($parent->attribute('TYPE')->value =~ /Vert/i) {
|
|
output "\n" unless $newline_last++;
|
|
output "\n";
|
|
}
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
########################################################################
|
|
#
|
|
# Stuff we don't know how to handle (yet)
|
|
#
|
|
########################################################################
|
|
|
|
# Address blocks:
|
|
|
|
# Credit stuff:
|
|
# ACKNO
|
|
# ADDRESS
|
|
# AFFILIATION
|
|
# ARTPAGENUMS
|
|
# ATTRIBUTION
|
|
# AUTHORBLURB
|
|
# AUTHORGROUP
|
|
# OTHERCREDIT
|
|
# HONORIFIC
|
|
|
|
# Areas:
|
|
# AREA
|
|
# AREASET
|
|
# AREASPEC
|
|
|
|
|
|
|
|
|
|
|
|
########################################################################
|
|
#
|
|
# Linkage, cross references
|
|
#
|
|
########################################################################
|
|
|
|
# Print the URL
|
|
sgml('</ULINK>', sub {
|
|
output ' <URL:', $_[0]->attribute('URL')->value, '>';
|
|
$newline_last = 0;
|
|
});
|
|
|
|
# If cross reference target is a RefEntry,
|
|
# output CiteRefEntry-style references.
|
|
sgml('<XREF>', sub {
|
|
my $id = $_[0]->attribute('LINKEND')->value;
|
|
my $manref = $Refs->get("refentry:$id");
|
|
|
|
if($manref) {
|
|
my ($title, $sect) = ($manref =~ /(.*)(\(.*\))/);
|
|
bold_on();
|
|
output $title;
|
|
font_off();
|
|
output $sect;
|
|
} else {
|
|
$blank_xrefs++ if $write_manpages;
|
|
output "[XRef to $id]";
|
|
}
|
|
|
|
$newline_last = 0;
|
|
});
|
|
|
|
# Anchor
|
|
|
|
|
|
|
|
|
|
########################################################################
|
|
#
|
|
# Other handlers
|
|
#
|
|
########################################################################
|
|
|
|
man_sgml('|[lt ]|', '<');
|
|
man_sgml('|[gt ]|', '>');
|
|
man_sgml('|[amp ]|', '&');
|
|
|
|
#
|
|
# Default handlers (uncomment these if needed). Right now, these are set
|
|
# up to gag on any unrecognised elements, sdata, processing-instructions,
|
|
# or entities.
|
|
#
|
|
# sgml('start_element',sub { die "Unknown element: " . $_[0]->name; });
|
|
# sgml('end_element','');
|
|
|
|
# This is for weeding out and escaping certain characters.
|
|
# This looks like it's inefficient since it's done on every line, but
|
|
# in reality, SGMLSpm and sgmlspl parsing ESIS takes _much_ longer.
|
|
|
|
sgml('cdata', sub
|
|
{
|
|
if(!$write_manpages) { return; }
|
|
elsif($raw_cdata) { output $_[0]; return; }
|
|
|
|
# Escape backslashes
|
|
$_[0] =~ s/\\/\\\\/g;
|
|
|
|
# In non-'pre'-type elements:
|
|
if(!$nocollapse_whitespace) {
|
|
# Change tabs to spaces
|
|
$_[0] =~ tr/\t/ /;
|
|
|
|
# Do not allow indents at beginning of line
|
|
# groff chokes on that.
|
|
if($newline_last) {
|
|
$_[0] =~ s/^ +//;
|
|
|
|
# If the line is all blank, don't do anything.
|
|
if($_[0] eq '') { return; }
|
|
|
|
$_[0] =~ s/^\./\\\&\./;
|
|
|
|
# Argh... roff doesn't like ' either...
|
|
$_[0] =~ s/^\'/\\\&\'/;
|
|
}
|
|
}
|
|
|
|
$newline_last = 0;
|
|
|
|
output $_[0];
|
|
});
|
|
|
|
|
|
# When in whitespace-collapsing mode, we disallow consecutive newlines.
|
|
|
|
sgml('re', sub
|
|
{
|
|
if($nocollapse_whitespace || !$newline_last) {
|
|
output "\n";
|
|
}
|
|
|
|
$newline_last = 1;
|
|
});
|
|
|
|
sgml('sdata',sub
|
|
{
|
|
if($_[0] =~ /\[minus \]/) { output "-"; }
|
|
elsif($_[0] =~ /\[copy \]/) { output "(C)"; }
|
|
elsif($_[0] =~ /\[nbsp \]/) { output " "; }
|
|
else { die "Unknown SDATA: " . $_[0]; }
|
|
});
|
|
sgml('pi',sub { die "Unknown processing instruction: " . $_[0]; });
|
|
sgml('entity',sub { die "Unknown external entity: " . $_[0]->name; });
|
|
sgml('start_subdoc',sub { die "Unknown subdoc entity: " . $_[0]->name; });
|
|
sgml('end_subdoc','');
|
|
sgml('conforming','');
|
|
|
|
1;
|
|
|