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