blob: 343d8a0915182b8118701f105b3569eb30d712fd [file] [log] [blame]
Lorenzo Colitti313379e2013-07-11 01:07:11 +09001=head1 NAME
2
3docbook2man-spec - convert DocBook RefEntries to Unix manpages
4
5=head1 SYNOPSIS
6
7The SGMLSpm package from CPAN. This contains the sgmlspl script which
8is used to grok this file. Use it like this:
9
10nsgmls some-docbook-document.sgml | sgmlspl docbook2man-spec.pl
11
12=head1 DESCRIPTION
13
14This is a sgmlspl spec file that produces Unix-style
15manpages from RefEntry markup.
16
17See the accompanying RefEntry man page for 'plain new' documentation. :)
18
19=head1 LIMITATIONS
20
21Trying docbook2man on non-DocBook or non-conformant SGML results in
22undefined behavior. :-)
23
24This program is a slow, dodgy Perl script.
25
26This program does not come close to supporting all the possible markup
27in DocBook, and will produce wrong output in some cases with supported
28markup.
29
30=head1 TODO
31
32Add new element handling and fix existing handling. Be robust.
33Produce cleanest, readable man output as possible (unlike some
34other converters). Follow Linux man(7) convention.
35If this results in added logic in this script,
36that's okay. The code should still be reasonably organized.
37
38Make it faster. If Perl sucks port it to another language.
39
40=head1 COPYRIGHT
41
42Copyright (C) 1998-1999 Steve Cheng <steve@ggi-project.org>
43
44This program is free software; you can redistribute it and/or modify it
45under the terms of the GNU General Public License as published by the Free
46Software Foundation; either version 2, or (at your option) any later
47version.
48
49You should have received a copy of the GNU General Public License along with
50this program; see the file COPYING. If not, please write to the Free
51Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
52
53=cut
54
55# $Id: docbook2man-spec.pl,v 1.1 2000/07/21 20:22:30 rosalia Exp $
56
57use SGMLS; # Use the SGMLS package.
58use SGMLS::Output; # Use stack-based output.
59use SGMLS::Refs;
60
61########################################################################
62# SGMLSPL script produced automatically by the script sgmlspl.pl
63#
64# Document Type: any, but processes only RefEntries
65# Edited by: me :)
66########################################################################
67
68$write_manpages = 0;
69$blank_xrefs = 0;
70
71sgml('start', sub {
72 push_output('nul');
73 $raw_cdata = 1; # Makes it a bit faster.
74
75 # Links file
76 open(LINKSFILE, ">manpage.links");
77
78 $Refs = new SGMLS::Refs("manpage.refs");
79});
80sgml('end', sub {
81 close(LINKSFILE);
82 if($blank_xrefs) {
83 print STDERR "Warning: output contains unresolved XRefs\n";
84 }
85});
86
87
88
89
90########################################################################
91#
92# Output helpers
93#
94########################################################################
95
96# Our own version of sgml() and output() to allow simple string output
97# to play well with roff's stupid whitespace rules.
98
99sub man_sgml
100{
101 if(ref($_[1]) eq 'CODE') {
102 return &sgml;
103 }
104
105 my $s = $_[1];
106
107 $s =~ s/\\/\\\\/g;
108 $s =~ s/'/\\'/g;
109
110 # \n at the beginning means start at beginning of line
111 if($s =~ s/^\n//) {
112 $sub = 'sub { output "\n" unless $newline_last++; ';
113 if($s eq '') {
114 sgml($_[0], eval('sub { output "\n" unless $newline_last++; }'));
115 } elsif($s =~ /\n$/) {
116 sgml($_[0], eval("sub { output \"\\n\" unless \$newline_last++; output '$s'; }"));
117 } else {
118 sgml($_[0], eval("sub { output \"\\n\" unless \$newline_last; output '$s'; \$newline_last = 0; }"));
119 }
120 } else {
121 if($s =~ /\n$/) {
122 sgml($_[0], eval("sub { output '$s'; \$newline_last = 1; }"));
123 } else {
124 sgml($_[0], eval("sub { output '$s'; \$newline_last = 0; }"));
125 }
126 }
127}
128
129sub man_output
130{
131 $_ = shift;
132 if(s/^\n//) {
133 output "\n" unless $newline_last++;
134 }
135 return if $_ eq '';
136
137 output $_;
138
139 if(@_) {
140 output @_;
141 $newline_last = (pop(@_) =~ /\n$/);
142 } else {
143 $newline_last = ($_ =~ /\n$/)
144 }
145}
146
147# Fold lines into one, quote some characters
148sub fold_string
149{
150 $_ = shift;
151
152 s/\\/\\\\/g;
153 s/"/\\\&"/g;
154
155 # Change tabs to spaces
156 tr/\t\n/ /;
157
158 # Trim whitespace from beginning and end.
159 s/^ +//;
160 s/ +$//;
161
162 return $_;
163}
164
165sub save_cdata()
166{
167 $raw_cdata++;
168 push_output('string');
169}
170
171sub bold_on()
172{
173 # If the last font is also bold, don't change anything.
174 # Basically this is to just get more readable man output.
175 if($fontstack[$#fontstack] ne 'bold') {
176 if(!$raw_cdata) {
177 output '\fB';
178 $newline_last = 0;
179 }
180 }
181 push(@fontstack, 'bold');
182}
183
184sub italic_on()
185{
186 # If the last font is also italic, don't change anything.
187 if($fontstack[$#fontstack] ne 'italic') {
188 if(!$raw_cdata) {
189 output '\fI';
190 $newline_last = 0;
191 }
192 }
193 push(@fontstack, 'italic');
194}
195
196sub font_off()
197{
198 my $thisfont = pop(@fontstack);
199 my $lastfont = $fontstack[$#fontstack];
200
201 # Only output font change if it is different
202 if($thisfont ne $lastfont) {
203 if($raw_cdata) { return; }
204 elsif($lastfont eq 'bold') { output '\fB'; }
205 elsif($lastfont eq 'italic') { output '\fI'; }
206 else { output '\fR'; }
207
208 $newline_last = 0;
209 }
210}
211
212
213
214
215
216
217########################################################################
218#
219# Manpage management
220#
221########################################################################
222
223sgml('<REFENTRY>', sub {
224 # This will be overwritten at end of REFMETA, when we know the name of the page.
225 pop_output();
226
227 $write_manpages = 1; # Currently writing manpage.
228
229 $nocollapse_whitespace = 0; # Current whitespace collapse counter.
230 $newline_last = 1; # At beginning of line?
231 # Just a bit of warning, you will see this variable manipulated
232 # manually a lot. It makes the code harder to follow but it
233 # saves you from having to worry about collapsing at the end of
234 # parse, stopping at verbatims, etc.
235 $raw_cdata = 0; # Instructs certain output functions to
236 # leave CDATA alone, so we can assign
237 # it to a string and process it, etc.
238 @fontstack = (); # Fonts being activated.
239
240 $manpage_title = ''; # Needed for indexing.
241 $manpage_sect = '';
242 @manpage_names = ();
243
244 $manpage_misc = '';
245
246 $list_nestlevel = 0; # Indent certain nested content.
247});
248sgml('</REFENTRY>', sub {
249 if(!$newline_last) {
250 output "\n";
251 }
252
253 $write_manpages = 0;
254 $raw_cdata = 1;
255 push_output('nul');
256});
257
258sgml('</REFMETA>', sub {
259 push_output('file', "$manpage_title.$manpage_sect");
260
261 output <<_END_BANNER;
262.\\" This manpage has been automatically generated by docbook2man
263.\\" from a DocBook document. This tool can be found at:
264.\\" <http://shell.ipoline.com/~elmert/comp/docbook2X/>
265.\\" Please send any bug reports, improvements, comments, patches,
266.\\" etc. to Steve Cheng <steve\@ggi-project.org>.
267_END_BANNER
268
269 my $manpage_date = `date "+%d %B %Y"`;
270
271 output '.TH "';
272
273 # If the title is not mixed-case, convention says to
274 # uppercase the whole title. (The canonical title is
275 # lowercase.)
276 if($manpage_title =~ /[A-Z]/) {
277 output fold_string($manpage_title);
278 } else {
279 output uc(fold_string($manpage_title));
280 }
281
282 output '" "', fold_string($manpage_sect),
283 '" "', fold_string(`date "+%d %B %Y"`),
284 '" "', $manpage_misc,
285 '" "', $manpage_manual,
286 "\"\n";
287
288 $newline_last = 1;
289
290 # References to this RefEntry.
291 my $id = $_[0]->parent->attribute('ID')->value;
292 if($id ne '') {
293 # The 'package name' part of the section should
294 # not be used when citing it.
295 my ($sectnum) = ($manpage_sect =~ /([0-9]*)/);
296
297 if($_[0]->parent->attribute('XREFLABEL')->value eq '') {
298 $Refs->put("refentry:$id", "$manpage_title($sectnum)");
299 } else {
300 $Refs->put("refentry:$id",
301 $_[0]->parent->attribute('XREFLABEL')->value .
302 "($sectnum)");
303 }
304 }
305});
306
307sgml('<REFENTRYTITLE>', sub {
308 if($_[0]->in('REFMETA')) {
309 save_cdata();
310 } else {
311 # Manpage citations are in bold.
312 bold_on();
313 }
314});
315sgml('</REFENTRYTITLE>', sub {
316 if($_[0]->in('REFMETA')) {
317 $raw_cdata--;
318 $manpage_title = pop_output();
319 }
320 else { font_off(); }
321});
322
323sgml('<MANVOLNUM>', sub {
324 if($_[0]->in('REFMETA')) {
325 save_cdata();
326 } else {
327 # Manpage citations use ().
328 output '(';
329 }
330});
331sgml('</MANVOLNUM>', sub {
332 if($_[0]->in('REFMETA')) {
333 $raw_cdata--;
334 $manpage_sect = pop_output();
335 }
336 else { output ')' }
337});
338
339sgml('<REFMISCINFO>', \&save_cdata);
340sgml('</REFMISCINFO>', sub {
341 $raw_cdata--;
342 $manpage_misc = fold_string(pop_output());
343});
344
345
346# NAME section
347man_sgml('<REFNAMEDIV>', "\n.SH NAME\n");
348
349sgml('<REFNAME>', \&save_cdata);
350sgml('</REFNAME>', sub {
351 $raw_cdata--;
352 push(@manpage_names, pop_output());
353});
354
355sgml('<REFPURPOSE>', \&save_cdata);
356sgml('</REFPURPOSE>', sub {
357 $raw_cdata--;
358 my $manpage_purpose = fold_string(pop_output());
359
360 for(my $i = 0; $i < $#manpage_names; $i++) {
361 output fold_string($manpage_names[$i]), ', ';
362 }
363
364 output fold_string($manpage_names[$#manpage_names]);
365 output " \\- $manpage_purpose\n";
366
367 $newline_last = 1;
368
369 foreach(@manpage_names) {
370 # Don't link to itself
371 if($_ ne $manpage_title) {
372 print LINKSFILE "$manpage_title.$manpage_sect $_.$manpage_sect\n";
373 }
374 }
375});
376
377man_sgml('<REFCLASS>', "\n.sp\n");
378
379#RefDescriptor
380
381
382
383
384
385########################################################################
386#
387# SYNOPSIS section and synopses
388#
389########################################################################
390
391man_sgml('<REFSYNOPSISDIV>', "\n.SH SYNOPSIS\n");
392man_sgml('</REFSYNOPSISDIV>', "\n");
393
394## FIXME! Must be made into block elements!!
395#sgml('<FUNCSYNOPSIS>', \&bold_on);
396#sgml('</FUNCSYNOPSIS>', \&font_off);
397#sgml('<CMDSYNOPSIS>', \&bold_on);
398#sgml('</CMDSYNOPSIS>', \&font_off);
399
400man_sgml('<FUNCSYNOPSIS>', sub {
401 man_output("\n.sp\n");
402 bold_on();
403});
404man_sgml('</FUNCSYNOPSIS>', sub {
405 font_off();
406 man_output("\n");
407});
408
409man_sgml('<CMDSYNOPSIS>', "\n\n");
410man_sgml('</CMDSYNOPSIS>', "\n\n");
411
412man_sgml('<FUNCPROTOTYPE>', "\n.sp\n");
413
414# Arguments to functions. This is C convention.
415man_sgml('<PARAMDEF>', '(');
416man_sgml('</PARAMDEF>', ");\n");
417man_sgml('<VOID>', "(void);\n");
418
419
420
421sub arg_start
422{
423 # my $choice = $_[0]->attribute('CHOICE')->value;
424
425 # The content model for CmdSynopsis doesn't include #PCDATA,
426 # so we won't see any of the whitespace in the source file,
427 # so we have to add it after each component.
428 output ' ';
429
430 if($_[0]->attribute('CHOICE')->value =~ /opt/i) {
431 output '[';
432 }
433 bold_on();
434}
435sub arg_end
436{
437 font_off();
438 if($_[0]->attribute('REP')->value =~ /^Repeat/i) {
439 italic_on();
440 output ' ...';
441 font_off();
442 }
443 if($_[0]->attribute('CHOICE')->value =~ /opt/i) {
444 output ']';
445 }
446}
447
448sgml('<ARG>', \&arg_start);
449sgml('</ARG>', \&arg_end);
450sgml('<GROUP>', \&arg_start);
451sgml('</GROUP>', \&arg_end);
452
453sgml('<OPTION>', \&bold_on);
454sgml('</OPTION>', \&font_off);
455
456# FIXME: This is one _blank_ line.
457man_sgml('<SBR>', "\n\n");
458
459
460########################################################################
461#
462# General sections
463#
464########################################################################
465
466# The name of the section is handled by TITLE. This just sets
467# up the roff markup.
468man_sgml('<REFSECT1>', "\n.SH ");
469man_sgml('<REFSECT2>', "\n.SS ");
470man_sgml('<REFSECT3>', "\n.SS ");
471
472
473########################################################################
474#
475# Titles, metadata.
476#
477########################################################################
478
479sgml('<TITLE>', sub {
480 if($_[0]->in('REFERENCE') or $_[0]->in('BOOK')) {
481 $write_manpages = 1;
482 }
483 save_cdata();
484});
485sgml('</TITLE>', sub {
486 my $title = fold_string(pop_output());
487 $raw_cdata--;
488
489 if($_[0]->in('REFERENCE') or $_[0]->in('BOOK')) {
490 # We use TITLE of enclosing Reference or Book as manual name
491 $manpage_manual = $title;
492 $write_manpages = 0;
493 }
494 elsif(exists $_[0]->parent->ext->{'title'}) {
495 # By far the easiest case. Just fold the string as
496 # above, and then set the parent element's variable.
497 $_[0]->parent->ext->{'title'} = $title;
498 }
499 else {
500 # If the parent element's handlers are lazy,
501 # output the folded string for them :)
502 # We assume they want uppercase and a newline.
503 output '"', uc($title), "\"\n";
504 $newline_last = 1;
505 }
506});
507
508sgml('<ATTRIBUTION>', sub { push_output('string') });
509sgml('</ATTRIBUTION>', sub { $_[0]->parent->ext->{'attribution'} = pop_output(); });
510
511
512# IGNORE.
513sgml('<DOCINFO>', sub { push_output('nul'); });
514sgml('</DOCINFO>', sub { pop_output(); });
515sgml('<REFSECT1INFO>', sub { push_output('nul'); });
516sgml('</REFSECT1INFO>', sub { pop_output(); });
517sgml('<REFSECT2INFO>', sub { push_output('nul'); });
518sgml('</REFSECT2INFO>', sub { pop_output(); });
519sgml('<REFSECT3INFO>', sub { push_output('nul'); });
520sgml('</REFSECT3INFO>', sub { pop_output(); });
521
522sgml('<INDEXTERM>', sub { push_output('nul'); });
523sgml('</INDEXTERM>', sub { pop_output(); });
524
525
526########################################################################
527#
528# Set bold on enclosed content
529#
530########################################################################
531
532sgml('<APPLICATION>', \&bold_on); sgml('</APPLICATION>', \&font_off);
533
534sgml('<CLASSNAME>', \&bold_on); sgml('</CLASSNAME>', \&font_off);
535sgml('<STRUCTNANE>', \&bold_on); sgml('</STRUCTNAME>', \&font_off);
536sgml('<STRUCTFIELD>', \&bold_on); sgml('</STRUCTFIELD>', \&font_off);
537sgml('<SYMBOL>', \&bold_on); sgml('</SYMBOL>', \&font_off);
538sgml('<TYPE>', \&bold_on); sgml('</TYPE>', \&font_off);
539
540sgml('<ENVAR>', \&bold_on); sgml('</ENVAR>', \&font_off);
541
542sgml('<FUNCTION>', \&bold_on); sgml('</FUNCTION>', \&font_off);
543
544sgml('<EMPHASIS>', \&bold_on); sgml('</EMPHASIS>', \&font_off);
545
546sgml('<ERRORNAME>', \&bold_on); sgml('</ERRORNAME>', \&font_off);
547# ERRORTYPE
548
549sgml('<COMMAND>', \&bold_on); sgml('</COMMAND>', \&font_off);
550
551sgml('<GUIBUTTON>', \&bold_on); sgml('</GUIBUTTON>', \&font_off);
552sgml('<GUIICON>', \&bold_on); sgml('</GUIICON>', \&font_off);
553# GUILABEL
554# GUIMENU
555# GUIMENUITEM
556# GUISUBMENU
557# MENUCHOICE
558# MOUSEBUTTON
559
560sgml('<ACCEL>', \&bold_on); sgml('</ACCEL>', \&font_off);
561sgml('<KEYCAP>', \&bold_on); sgml('</KEYCAP>', \&font_off);
562sgml('<KEYSYM>', \&bold_on); sgml('</KEYSYM>', \&font_off);
563# KEYCODE
564# KEYCOMBO
565# SHORTCUT
566
567sgml('<USERINPUT>', \&bold_on); sgml('</USERINPUT>', \&font_off);
568
569sgml('<INTERFACEDEFINITION>', \&bold_on);
570sgml('</INTERFACEDEFINITION>', \&font_off);
571
572# May need to look at the CLASS
573sgml('<SYSTEMITEM>', \&bold_on);
574sgml('</SYSTEMITEM>', \&font_off);
575
576
577
578
579
580########################################################################
581#
582# Set italic on enclosed content
583#
584########################################################################
585
586sgml('<FIRSTTERM>', \&italic_on); sgml('</FIRSTTERM>', \&font_off);
587
588sgml('<FILENAME>', \&italic_on); sgml('</FILENAME>', \&font_off);
589sgml('<PARAMETER>', \&italic_on); sgml('</PARAMETER>', \&font_off);
590sgml('<PROPERTY>', \&italic_on); sgml('</PROPERTY>', \&font_off);
591
592sgml('<REPLACEABLE>', sub {
593 italic_on();
594 if($_[0]->in('TOKEN')) {
595 # When tokenizing, follow more 'intuitive' convention
596 output "<";
597 }
598});
599sgml('</REPLACEABLE>', sub {
600 if($_[0]->in('TOKEN')) {
601 output ">";
602 }
603 font_off();
604});
605
606sgml('<CITETITLE>', \&italic_on); sgml('</CITETITLE>', \&font_off);
607sgml('<FOREIGNPHRASE>', \&italic_on); sgml('</FOREIGNPHRASE>', \&font_off);
608
609sgml('<LINEANNOTATION>', \&italic_on); sgml('</LINEANNOTATION>', \&font_off);
610
611
612
613
614
615
616########################################################################
617#
618# Other 'inline' elements
619#
620########################################################################
621
622man_sgml('<EMAIL>', '<');
623man_sgml('</EMAIL>', '>');
624man_sgml('<OPTIONAL>', '[');
625man_sgml('</OPTIONAL>', ']');
626
627man_sgml('</TRADEMARK>', "\\u\\s-2TM\\s+2\\d");
628
629man_sgml('<COMMENT>', "[Comment: ");
630man_sgml('</COMMENT>', "]");
631
632man_sgml('<QUOTE>', "``");
633man_sgml('</QUOTE>', "''");
634
635#man_sgml('<LITERAL>', '"');
636#man_sgml('</LITERAL>', '"');
637
638# No special presentation:
639
640# AUTHOR
641# AUTHORINITIALS
642
643# ABBREV
644# ACTION
645# ACRONYM
646# ALT
647# CITATION
648# PHRASE
649# QUOTE
650# WORDASWORD
651
652# COMPUTEROUTPUT
653# MARKUP
654# PROMPT
655# RETURNVALUE
656# SGMLTAG
657# TOKEN
658
659# DATABASE
660# HARDWARE
661# INTERFACE
662# MEDIALABEL
663
664# There doesn't seem to be a good way to represent LITERAL in -man
665
666
667
668########################################################################
669#
670# Paragraph and paragraph-like elements
671#
672########################################################################
673
674sub para_start {
675 output "\n" unless $newline_last++;
676
677 # In lists, etc., don't start paragraph with .PP since
678 # the indentation will be gone.
679
680 if($_[0]->parent->ext->{'nobreak'}==1) {
681 # Usually this is the FIRST element of
682 # a hanging tag, so we MUST not do a full
683 # paragraph break.
684 $_[0]->parent->ext->{'nobreak'} = 2;
685 } elsif($_[0]->parent->ext->{'nobreak'}==2) {
686 # Usually these are the NEXT elements of
687 # a hanging tag. If we break using a blank
688 # line, we're okay.
689 output "\n";
690 } else {
691 # Normal case. (For indented blocks too, at least
692 # -man isn't so braindead in this area.)
693 output ".PP\n";
694 }
695}
696# Actually applies to a few other block elements as well
697sub para_end {
698 output "\n" unless $newline_last++;
699}
700
701sgml('<PARA>', \&para_start);
702sgml('</PARA>', \&para_end);
703sgml('<SIMPARA>', \&para_start);
704sgml('</SIMPARA>', \&para_end);
705
706# Nothing special, except maybe FIXME set nobreak.
707sgml('<INFORMALEXAMPLE>', \&para_start);
708sgml('</INFORMALEXAMPLE>', \&para_end);
709
710
711
712
713
714########################################################################
715#
716# Blocks using SS sections
717#
718########################################################################
719
720# FIXME: We need to consider the effects of SS
721# in a hanging tag :(
722
723# Complete with the optional-title dilemma (again).
724sgml('<ABSTRACT>', sub {
725 $_[0]->ext->{'title'} = 'ABSTRACT';
726 output "\n" unless $newline_last++;
727 push_output('string');
728});
729sgml('</ABSTRACT>', sub {
730 my $content = pop_output();
731
732 # As ABSTRACT is never on the same level as RefSect1,
733 # this leaves us with only .SS in terms of -man macros.
734 output ".SS \"", uc($_[0]->ext->{'title'}), "\"\n";
735
736 output $content;
737 output "\n" unless $newline_last++;
738});
739
740# Ah, I needed a break. Example always has a title.
741man_sgml('<EXAMPLE>', "\n.SS ");
742sgml('</EXAMPLE>', \&para_end);
743
744# Same with sidebar.
745man_sgml('<SIDEBAR>', "\n.SS ");
746sgml('</SIDEBAR>', \&para_end);
747
748# NO title.
749man_sgml('<HIGHLIGHTS>', "\n.SS HIGHLIGHTS\n");
750sgml('</HIGHLIGHTS>', \&para_end);
751
752
753
754
755########################################################################
756#
757# Indented 'Block' elements
758#
759########################################################################
760
761sub indent_block_start
762{
763 output "\n" unless $newline_last++;
764 output ".sp\n.RS\n";
765}
766sub indent_block_end
767{
768 output "\n" unless $newline_last++;
769 output ".RE\n";
770}
771
772# This element is almost like an admonition (below),
773# only the default title is blank :)
774
775sgml('<BLOCKQUOTE>', sub {
776 $_[0]->ext->{'title'} = '';
777 output "\n" unless $newline_last++;
778 push_output('string');
779});
780sgml('</BLOCKQUOTE>', sub {
781 my $content = pop_output();
782
783 indent_block_start();
784
785 if($_[0]->ext->{'title'}) {
786 output ".B \"", $_[0]->ext->{'title'}, ":\"\n";
787 }
788
789 output $content;
790
791 if($_[0]->ext->{'attribution'}) {
792 output "\n" unless $newline_last++;
793 # One place where roff's space-sensitivity makes sense :)
794 output "\n -- ";
795 output $_[0]->ext->{'attribution'} . "\n";
796 }
797
798 indent_block_end();
799});
800
801# Set off admonitions from the rest of the text by indenting.
802# FIXME: Need to check if this works inside paragraphs, not enclosing them.
803sub admonition_end {
804 my $content = pop_output();
805
806 indent_block_start();
807
808 # When the admonition is only one paragraph,
809 # it looks nicer if the title was inline.
810 my $num_para;
811 while ($content =~ /^\.PP/gm) { $num_para++ }
812 if($num_para==1) {
813 $content =~ s/^\.PP\n//;
814 }
815
816 output ".B \"" . $_[0]->ext->{'title'} . ":\"\n";
817 output $content;
818
819 indent_block_end();
820}
821
822sgml('<NOTE>', sub {
823 # We can't see right now whether or not there is a TITLE
824 # element, so we have to save the output now and add it back
825 # at the end of this admonition.
826 $_[0]->ext->{'title'} = 'Note';
827
828 # Although admonition_end's indent_block_start will do this,
829 # we need to synchronize the output _now_
830 output "\n" unless $newline_last++;
831
832 push_output('string');
833});
834sgml('</NOTE>', \&admonition_end);
835
836# Same as above.
837sgml('<WARNING>', sub {
838 $_[0]->ext->{'title'} = 'Warning';
839 output "\n" unless $newline_last++;
840 push_output('string');
841});
842sgml('</WARNING>', \&admonition_end);
843
844sgml('<TIP>', sub {
845 $_[0]->ext->{'title'} = 'Tip';
846 output "\n" unless $newline_last++;
847 push_output('string');
848});
849sgml('</TIP>', \&admonition_end);
850sgml('<CAUTION>', sub {
851 $_[0]->ext->{'title'} = 'Caution';
852 output "\n" unless $newline_last++;
853 push_output('string');
854});
855sgml('</CAUTION>', \&admonition_end);
856
857sgml('<IMPORTANT>', sub {
858 $_[0]->ext->{'title'} = 'Important';
859 output "\n" unless $newline_last++;
860 push_output('string');
861});
862sgml('</IMPORTANT>', \&admonition_end);
863
864
865
866
867
868
869
870
871
872
873
874
875########################################################################
876#
877# Verbatim displays.
878#
879########################################################################
880
881sub verbatim_start {
882 output "\n" unless $newline_last++;
883
884 if($_[0]->parent->ext->{'nobreak'}==1) {
885 # Usually this is the FIRST element of
886 # a hanging tag, so we MUST not do a full
887 # paragraph break.
888 $_[0]->parent->ext->{'nobreak'} = 2;
889 } else {
890 output "\n";
891 }
892
893 output(".nf\n") unless $nocollapse_whitespace++;
894}
895
896sub verbatim_end {
897 output "\n" unless $newline_last++;
898 output(".fi\n") unless --$nocollapse_whitespace;
899}
900
901sgml('<PROGRAMLISTING>', \&verbatim_start);
902sgml('</PROGRAMLISTING>', \&verbatim_end);
903
904sgml('<SCREEN>', \&verbatim_start);
905sgml('</SCREEN>', \&verbatim_end);
906
907sgml('<LITERALLAYOUT>', \&verbatim_start);
908sgml('</LITERALLAYOUT>', \&verbatim_end);
909
910#sgml('<SYNOPSIS>', sub {
911# if($_[0]->attribute('FORMAT')->value =~ /linespecific/i) {
912# &verbatim_start;
913# } else {
914# roffcmd("");
915# }
916#});
917#
918#sgml('</SYNOPSIS>', sub {
919# if($_[0]->attribute('FORMAT')->value =~ /linespecific/i) {
920# &verbatim_end;
921# }
922# else {
923# roffcmd("");# not sure about this.
924# }
925#});
926sgml('<SYNOPSIS>', \&verbatim_start);
927sgml('</SYNOPSIS>', \&verbatim_end);
928
929
930
931
932
933
934
935
936
937########################################################################
938#
939# Lists
940#
941########################################################################
942
943# Indent nested lists.
944sub indent_list_start {
945 if($list_nestlevel++) {
946 output "\n" unless $newline_last++;
947 output ".RS\n";
948 }
949}
950sub indent_list_end {
951 if(--$list_nestlevel) {
952 output "\n" unless $newline_last++;
953 output ".RE\n";
954 }
955}
956
957sgml('<VARIABLELIST>', \&indent_list_start);
958sgml('</VARIABLELIST>', \&indent_list_end);
959sgml('<ITEMIZEDLIST>', \&indent_list_start);
960sgml('</ITEMIZEDLIST>', \&indent_list_end);
961sgml('<ORDEREDLIST>', sub {
962 indent_list_start();
963 $_[0]->ext->{'count'} = 1;
964});
965sgml('</ORDEREDLIST>', \&indent_list_end);
966
967# Output content on one line, bolded.
968sgml('<TERM>', sub {
969 output "\n" unless $newline_last++;
970 output ".TP\n";
971 bold_on();
972 push_output('string');
973});
974sgml('</TERM>', sub {
975 my $term = pop_output();
976 $term =~ tr/\n/ /;
977 output $term;
978 font_off();
979 output "\n";
980 $newline_last = 1;
981});
982
983sgml('<LISTITEM>', sub {
984 # A bulleted list.
985 if($_[0]->in('ITEMIZEDLIST')) {
986 output "\n" unless $newline_last++;
987 output ".TP 0.2i\n\\(bu\n";
988 }
989
990 # Need numbers.
991 # Assume Arabic numeration for now.
992 elsif($_[0]->in('ORDEREDLIST')) {
993 output "\n" unless $newline_last++;
994 output ".TP ", $_[0]->parent->ext->{'count'}++, ". \n";
995 }
996
997 $_[0]->ext->{'nobreak'} = 1;
998});
999
1000sgml('<SIMPLELIST>', sub {
1001 $_[0]->ext->{'first_member'} = 1;
1002});
1003
1004sgml('<MEMBER>', sub {
1005 my $parent = $_[0]->parent;
1006
1007 if($parent->attribute('TYPE')->value =~ /Inline/i) {
1008 if($parent->ext->{'first_member'}) {
1009 # If this is the first member don't put any commas
1010 $parent->ext->{'first_member'} = 0;
1011 } else {
1012 output ", ";
1013 }
1014 } elsif($parent->attribute('TYPE')->value =~ /Vert/i) {
1015 output "\n" unless $newline_last++;
1016 output "\n";
1017 }
1018});
1019
1020
1021
1022
1023
1024########################################################################
1025#
1026# Stuff we don't know how to handle (yet)
1027#
1028########################################################################
1029
1030# Address blocks:
1031
1032# Credit stuff:
1033# ACKNO
1034# ADDRESS
1035# AFFILIATION
1036# ARTPAGENUMS
1037# ATTRIBUTION
1038# AUTHORBLURB
1039# AUTHORGROUP
1040# OTHERCREDIT
1041# HONORIFIC
1042
1043# Areas:
1044# AREA
1045# AREASET
1046# AREASPEC
1047
1048
1049
1050
1051
1052########################################################################
1053#
1054# Linkage, cross references
1055#
1056########################################################################
1057
1058# Print the URL
1059sgml('</ULINK>', sub {
1060# output ' <URL:', $_[0]->attribute('URL')->value, '>';
1061 $newline_last = 0;
1062});
1063
1064# If cross reference target is a RefEntry,
1065# output CiteRefEntry-style references.
1066sgml('<XREF>', sub {
1067 my $id = $_[0]->attribute('LINKEND')->value;
1068 my $manref = $Refs->get("refentry:$id");
1069
1070 if($manref) {
1071 my ($title, $sect) = ($manref =~ /(.*)(\(.*\))/);
1072 bold_on();
1073 output $title;
1074 font_off();
1075 output $sect;
1076 } else {
1077 $blank_xrefs++ if $write_manpages;
1078 output "[XRef to $id]";
1079 }
1080
1081 $newline_last = 0;
1082});
1083
1084# Anchor
1085
1086
1087
1088
1089########################################################################
1090#
1091# Other handlers
1092#
1093########################################################################
1094
1095man_sgml('|[lt ]|', '<');
1096man_sgml('|[gt ]|', '>');
1097man_sgml('|[amp ]|', '&');
1098
1099#
1100# Default handlers (uncomment these if needed). Right now, these are set
1101# up to gag on any unrecognised elements, sdata, processing-instructions,
1102# or entities.
1103#
1104# sgml('start_element',sub { die "Unknown element: " . $_[0]->name; });
1105# sgml('end_element','');
1106
1107# This is for weeding out and escaping certain characters.
1108# This looks like it's inefficient since it's done on every line, but
1109# in reality, SGMLSpm and sgmlspl parsing ESIS takes _much_ longer.
1110
1111sgml('cdata', sub
1112{
1113 if(!$write_manpages) { return; }
1114 elsif($raw_cdata) { output $_[0]; return; }
1115
1116 # Escape backslashes
1117 $_[0] =~ s/\\/\\\\/g;
1118
1119 # In non-'pre'-type elements:
1120 if(!$nocollapse_whitespace) {
1121 # Change tabs to spaces
1122 $_[0] =~ tr/\t/ /;
1123
1124 # Do not allow indents at beginning of line
1125 # groff chokes on that.
1126 if($newline_last) {
1127 $_[0] =~ s/^ +//;
1128
1129 # If the line is all blank, don't do anything.
1130 if($_[0] eq '') { return; }
1131
1132 $_[0] =~ s/^\./\\\&\./;
1133
1134 # Argh... roff doesn't like ' either...
1135 $_[0] =~ s/^\'/\\\&\'/;
1136 }
1137 }
1138
1139 $newline_last = 0;
1140
1141 output $_[0];
1142});
1143
1144
1145# When in whitespace-collapsing mode, we disallow consecutive newlines.
1146
1147sgml('re', sub
1148{
1149 if($nocollapse_whitespace || !$newline_last) {
1150 output "\n";
1151 }
1152
1153 $newline_last = 1;
1154});
1155
1156sgml('sdata',sub { die "Unknown SDATA: " . $_[0]; });
1157sgml('pi',sub { die "Unknown processing instruction: " . $_[0]; });
1158sgml('entity',sub { die "Unknown external entity: " . $_[0]->name; });
1159sgml('start_subdoc',sub { die "Unknown subdoc entity: " . $_[0]->name; });
1160sgml('end_subdoc','');
1161sgml('conforming','');
1162
11631;
1164