TIVI-153: no iputils in Tizen; initial commit
[profile/ivi/iputils.git] / doc / 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.1 2000/07/21 20:22:30 rosalia 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 sgml('</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
258 sgml('</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
307 sgml('<REFENTRYTITLE>', sub { 
308         if($_[0]->in('REFMETA')) { 
309                 save_cdata();
310         } else { 
311                 # Manpage citations are in bold.
312                 bold_on();
313         }
314 });
315 sgml('</REFENTRYTITLE>', sub { 
316         if($_[0]->in('REFMETA')) {
317                 $raw_cdata--;
318                 $manpage_title = pop_output();
319         }
320         else { font_off(); }
321 });
322
323 sgml('<MANVOLNUM>', sub { 
324         if($_[0]->in('REFMETA')) { 
325                 save_cdata();   
326         } else {
327                 # Manpage citations use ().
328                 output '(';
329         }
330 });
331 sgml('</MANVOLNUM>', sub { 
332         if($_[0]->in('REFMETA')) {
333                 $raw_cdata--;
334                 $manpage_sect = pop_output();
335         }
336         else { output ')' }
337 });
338
339 sgml('<REFMISCINFO>', \&save_cdata);
340 sgml('</REFMISCINFO>', sub { 
341         $raw_cdata--;
342         $manpage_misc = fold_string(pop_output());
343 });
344
345
346 # NAME section
347 man_sgml('<REFNAMEDIV>', "\n.SH NAME\n");
348
349 sgml('<REFNAME>', \&save_cdata);
350 sgml('</REFNAME>', sub { 
351         $raw_cdata--;
352         push(@manpage_names, pop_output());
353 });
354
355 sgml('<REFPURPOSE>', \&save_cdata);
356 sgml('</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         
377 man_sgml('<REFCLASS>', "\n.sp\n");
378
379 #RefDescriptor
380
381
382
383
384
385 ########################################################################
386 #
387 # SYNOPSIS section and synopses
388 #
389 ########################################################################
390
391 man_sgml('<REFSYNOPSISDIV>', "\n.SH SYNOPSIS\n");
392 man_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
400 man_sgml('<FUNCSYNOPSIS>', sub {
401         man_output("\n.sp\n");
402         bold_on();
403 });
404 man_sgml('</FUNCSYNOPSIS>', sub {
405         font_off();
406         man_output("\n");
407 });
408
409 man_sgml('<CMDSYNOPSIS>', "\n\n");
410 man_sgml('</CMDSYNOPSIS>', "\n\n");
411
412 man_sgml('<FUNCPROTOTYPE>', "\n.sp\n");
413
414 # Arguments to functions.  This is C convention.
415 man_sgml('<PARAMDEF>', '(');
416 man_sgml('</PARAMDEF>', ");\n");
417 man_sgml('<VOID>', "(void);\n");
418
419
420
421 sub 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 }
435 sub 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
448 sgml('<ARG>', \&arg_start);
449 sgml('</ARG>', \&arg_end);
450 sgml('<GROUP>', \&arg_start);
451 sgml('</GROUP>', \&arg_end);
452
453 sgml('<OPTION>', \&bold_on);
454 sgml('</OPTION>', \&font_off);
455
456 # FIXME: This is one _blank_ line.
457 man_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.
468 man_sgml('<REFSECT1>', "\n.SH ");
469 man_sgml('<REFSECT2>', "\n.SS ");
470 man_sgml('<REFSECT3>', "\n.SS ");
471
472
473 ########################################################################
474 #
475 # Titles, metadata.
476 #
477 ########################################################################
478
479 sgml('<TITLE>', sub {
480         if($_[0]->in('REFERENCE') or $_[0]->in('BOOK')) {
481                 $write_manpages = 1;
482         }
483         save_cdata();
484 });
485 sgml('</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
508 sgml('<ATTRIBUTION>', sub { push_output('string') });
509 sgml('</ATTRIBUTION>', sub { $_[0]->parent->ext->{'attribution'} = pop_output(); });
510
511
512 # IGNORE.
513 sgml('<DOCINFO>', sub { push_output('nul'); });
514 sgml('</DOCINFO>', sub { pop_output(); });
515 sgml('<REFSECT1INFO>', sub { push_output('nul'); });
516 sgml('</REFSECT1INFO>', sub { pop_output(); });
517 sgml('<REFSECT2INFO>', sub { push_output('nul'); });
518 sgml('</REFSECT2INFO>', sub { pop_output(); });
519 sgml('<REFSECT3INFO>', sub { push_output('nul'); });
520 sgml('</REFSECT3INFO>', sub { pop_output(); });
521
522 sgml('<INDEXTERM>', sub { push_output('nul'); });
523 sgml('</INDEXTERM>', sub { pop_output(); });
524
525
526 ########################################################################
527 #
528 # Set bold on enclosed content 
529 #
530 ########################################################################
531
532 sgml('<APPLICATION>', \&bold_on);       sgml('</APPLICATION>', \&font_off);
533
534 sgml('<CLASSNAME>', \&bold_on);         sgml('</CLASSNAME>', \&font_off);
535 sgml('<STRUCTNANE>', \&bold_on);        sgml('</STRUCTNAME>', \&font_off);
536 sgml('<STRUCTFIELD>', \&bold_on);       sgml('</STRUCTFIELD>', \&font_off);
537 sgml('<SYMBOL>', \&bold_on);            sgml('</SYMBOL>', \&font_off);
538 sgml('<TYPE>', \&bold_on);              sgml('</TYPE>', \&font_off);
539
540 sgml('<ENVAR>', \&bold_on);     sgml('</ENVAR>', \&font_off);
541
542 sgml('<FUNCTION>', \&bold_on);  sgml('</FUNCTION>', \&font_off);
543
544 sgml('<EMPHASIS>', \&bold_on);  sgml('</EMPHASIS>', \&font_off);
545
546 sgml('<ERRORNAME>', \&bold_on); sgml('</ERRORNAME>', \&font_off);
547 # ERRORTYPE
548
549 sgml('<COMMAND>', \&bold_on);   sgml('</COMMAND>', \&font_off);
550
551 sgml('<GUIBUTTON>', \&bold_on); sgml('</GUIBUTTON>', \&font_off);
552 sgml('<GUIICON>', \&bold_on);   sgml('</GUIICON>', \&font_off);
553 # GUILABEL
554 # GUIMENU
555 # GUIMENUITEM
556 # GUISUBMENU
557 # MENUCHOICE
558 # MOUSEBUTTON
559
560 sgml('<ACCEL>', \&bold_on);     sgml('</ACCEL>', \&font_off);
561 sgml('<KEYCAP>', \&bold_on);    sgml('</KEYCAP>', \&font_off);
562 sgml('<KEYSYM>', \&bold_on);    sgml('</KEYSYM>', \&font_off);
563 # KEYCODE
564 # KEYCOMBO
565 # SHORTCUT
566
567 sgml('<USERINPUT>', \&bold_on); sgml('</USERINPUT>', \&font_off);
568
569 sgml('<INTERFACEDEFINITION>', \&bold_on);
570 sgml('</INTERFACEDEFINITION>', \&font_off);
571
572 # May need to look at the CLASS
573 sgml('<SYSTEMITEM>', \&bold_on);
574 sgml('</SYSTEMITEM>', \&font_off);
575
576
577
578
579
580 ########################################################################
581 #
582 # Set italic on enclosed content 
583 #
584 ########################################################################
585
586 sgml('<FIRSTTERM>', \&italic_on);       sgml('</FIRSTTERM>', \&font_off);
587
588 sgml('<FILENAME>', \&italic_on);        sgml('</FILENAME>', \&font_off);
589 sgml('<PARAMETER>', \&italic_on);       sgml('</PARAMETER>', \&font_off);
590 sgml('<PROPERTY>', \&italic_on);        sgml('</PROPERTY>', \&font_off);
591
592 sgml('<REPLACEABLE>', sub {
593         italic_on();
594         if($_[0]->in('TOKEN')) {
595                 # When tokenizing, follow more 'intuitive' convention
596                 output "<";
597         }
598 });
599 sgml('</REPLACEABLE>', sub {
600         if($_[0]->in('TOKEN')) {
601                 output ">";
602         }
603         font_off();
604 });
605
606 sgml('<CITETITLE>', \&italic_on);       sgml('</CITETITLE>', \&font_off);
607 sgml('<FOREIGNPHRASE>', \&italic_on);   sgml('</FOREIGNPHRASE>', \&font_off);
608
609 sgml('<LINEANNOTATION>', \&italic_on);  sgml('</LINEANNOTATION>', \&font_off);
610
611
612
613
614
615
616 ########################################################################
617 #
618 # Other 'inline' elements 
619 #
620 ########################################################################
621
622 man_sgml('<EMAIL>', '<');
623 man_sgml('</EMAIL>', '>');
624 man_sgml('<OPTIONAL>', '[');
625 man_sgml('</OPTIONAL>', ']');
626
627 man_sgml('</TRADEMARK>', "\\u\\s-2TM\\s+2\\d");
628
629 man_sgml('<COMMENT>', "[Comment: ");
630 man_sgml('</COMMENT>', "]");
631
632 man_sgml('<QUOTE>', "``");
633 man_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
674 sub 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
697 sub para_end {
698         output "\n" unless $newline_last++; 
699 }
700
701 sgml('<PARA>', \&para_start);
702 sgml('</PARA>', \&para_end);
703 sgml('<SIMPARA>', \&para_start);
704 sgml('</SIMPARA>', \&para_end);
705
706 # Nothing special, except maybe FIXME set nobreak.
707 sgml('<INFORMALEXAMPLE>', \&para_start);
708 sgml('</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).
724 sgml('<ABSTRACT>', sub {
725         $_[0]->ext->{'title'} = 'ABSTRACT';
726         output "\n" unless $newline_last++;
727         push_output('string');
728 });
729 sgml('</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.
741 man_sgml('<EXAMPLE>', "\n.SS ");
742 sgml('</EXAMPLE>', \&para_end);
743
744 # Same with sidebar.
745 man_sgml('<SIDEBAR>', "\n.SS ");
746 sgml('</SIDEBAR>', \&para_end);
747
748 # NO title.
749 man_sgml('<HIGHLIGHTS>', "\n.SS HIGHLIGHTS\n");
750 sgml('</HIGHLIGHTS>', \&para_end);
751
752
753
754
755 ########################################################################
756 #
757 # Indented 'Block' elements 
758 #
759 ########################################################################
760
761 sub indent_block_start
762 {
763         output "\n" unless $newline_last++;
764         output ".sp\n.RS\n";
765 }
766 sub 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
775 sgml('<BLOCKQUOTE>', sub { 
776         $_[0]->ext->{'title'} = ''; 
777         output "\n" unless $newline_last++;
778         push_output('string');
779 });
780 sgml('</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.
803 sub 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
822 sgml('<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 });
834 sgml('</NOTE>', \&admonition_end);
835
836 # Same as above.
837 sgml('<WARNING>', sub { 
838         $_[0]->ext->{'title'} = 'Warning'; 
839         output "\n" unless $newline_last++;
840         push_output('string');
841 });
842 sgml('</WARNING>', \&admonition_end);
843
844 sgml('<TIP>', sub {
845         $_[0]->ext->{'title'} = 'Tip';
846         output "\n" unless $newline_last++;
847         push_output('string');
848 });
849 sgml('</TIP>', \&admonition_end);
850 sgml('<CAUTION>', sub {
851         $_[0]->ext->{'title'} = 'Caution';
852         output "\n" unless $newline_last++;
853         push_output('string');
854 });
855 sgml('</CAUTION>', \&admonition_end);
856
857 sgml('<IMPORTANT>', sub {
858         $_[0]->ext->{'title'} = 'Important';
859         output "\n" unless $newline_last++;
860         push_output('string');
861 });
862 sgml('</IMPORTANT>', \&admonition_end);
863
864
865
866
867
868
869
870
871
872
873
874
875 ########################################################################
876 #
877 # Verbatim displays. 
878 #
879 ########################################################################
880
881 sub 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
896 sub verbatim_end {
897         output "\n" unless $newline_last++;
898         output(".fi\n") unless --$nocollapse_whitespace;
899 }
900
901 sgml('<PROGRAMLISTING>', \&verbatim_start); 
902 sgml('</PROGRAMLISTING>', \&verbatim_end);
903
904 sgml('<SCREEN>', \&verbatim_start); 
905 sgml('</SCREEN>', \&verbatim_end);
906
907 sgml('<LITERALLAYOUT>', \&verbatim_start); 
908 sgml('</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 #});
926 sgml('<SYNOPSIS>', \&verbatim_start);
927 sgml('</SYNOPSIS>', \&verbatim_end);
928
929
930
931
932
933
934
935
936
937 ########################################################################
938 #
939 # Lists
940 #
941 ########################################################################
942
943 # Indent nested lists.
944 sub indent_list_start {
945         if($list_nestlevel++) {
946                 output "\n" unless $newline_last++;
947                 output ".RS\n";
948         }
949 }
950 sub indent_list_end {
951         if(--$list_nestlevel) {
952                 output "\n" unless $newline_last++;
953                 output ".RE\n";
954         }
955 }
956
957 sgml('<VARIABLELIST>', \&indent_list_start);
958 sgml('</VARIABLELIST>', \&indent_list_end);
959 sgml('<ITEMIZEDLIST>', \&indent_list_start);
960 sgml('</ITEMIZEDLIST>', \&indent_list_end);
961 sgml('<ORDEREDLIST>', sub { 
962         indent_list_start();
963         $_[0]->ext->{'count'} = 1;
964 });
965 sgml('</ORDEREDLIST>', \&indent_list_end);
966                 
967 # Output content on one line, bolded.
968 sgml('<TERM>', sub { 
969         output "\n" unless $newline_last++;
970         output ".TP\n";
971         bold_on();
972         push_output('string');
973 });
974 sgml('</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         
983 sgml('<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
1000 sgml('<SIMPLELIST>', sub {
1001         $_[0]->ext->{'first_member'} = 1;
1002 });
1003
1004 sgml('<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
1059 sgml('</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.
1066 sgml('<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
1095 man_sgml('|[lt    ]|', '<');
1096 man_sgml('|[gt    ]|', '>');
1097 man_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
1111 sgml('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
1147 sgml('re', sub
1148 {
1149         if($nocollapse_whitespace || !$newline_last) {
1150                 output "\n";
1151         }
1152
1153         $newline_last = 1;
1154 });
1155
1156 sgml('sdata',sub { die "Unknown SDATA: " . $_[0]; });
1157 sgml('pi',sub { die "Unknown processing instruction: " . $_[0]; });
1158 sgml('entity',sub { die "Unknown external entity: " . $_[0]->name; });
1159 sgml('start_subdoc',sub { die "Unknown subdoc entity: " . $_[0]->name; });
1160 sgml('end_subdoc','');
1161 sgml('conforming','');
1162
1163 1;
1164