mdoc2man revision 290000
1#! /usr/local/bin/perl
2
3## mdoc2man.pl -- Convert mdoc tags to man tags
4##
5## Author:	Harlan Stenn <stenn@ntp.org>
6##		
7##
8##  This file is part of AutoOpts, a companion to AutoGen.
9##  AutoOpts is free software.
10##  AutoOpts is Copyright (C) 1992-2015 by Bruce Korb - all rights reserved
11##
12##  AutoOpts is available under any one of two licenses.  The license
13##  in use must be one of these two and the choice is under the control
14##  of the user of the license.
15##
16##   The GNU Lesser General Public License, version 3 or later
17##      See the files "COPYING.lgplv3" and "COPYING.gplv3"
18##
19##   The Modified Berkeley Software Distribution License
20##      See the file "COPYING.mbsd"
21##
22##  These files have the following sha256 sums:
23##
24##  8584710e9b04216a394078dc156b781d0b47e1729104d666658aecef8ee32e95  COPYING.gplv3
25##  4379e7444a0e2ce2b12dd6f5a52a27a4d02d39d247901d3285c88cf0d37f477b  COPYING.lgplv3
26##  13aa749a5b0a454917a944ed8fffc530b784f5ead522b1aacaf4ec8aa55a6239  COPYING.mbsd
27
28### ToDo
29# Properly implement -columns in the "my %lists" definition...
30#
31# .Xr requires at least 1 arg, the code here expects at least 2
32#
33###
34
35package mdoc2man;
36use strict;
37use warnings;
38use File::Basename;
39use lib dirname(__FILE__);
40use Mdoc qw(hs ns pp mapwords son soff stoggle gen_encloser);
41
42########
43## Basic
44########
45
46Mdoc::def_macro( '.Sh', sub { '.SH', hs, @_ }, raw => 1);
47Mdoc::def_macro( '.Ss', sub { '.SS', hs, @_ }, raw => 1);
48Mdoc::def_macro( '.Pp', sub { ".sp \\n(Ppu\n.ne 2\n" } );
49Mdoc::def_macro( '.Nd', sub { "\\- @_" } );
50
51# Macros that enclose things
52Mdoc::def_macro( '.Brq', gen_encloser(qw({ }))          , greedy => 1 );
53Mdoc::def_macro( '.Op' , gen_encloser(qw([ ]))          , greedy => 1 );
54Mdoc::def_macro( '.Qq' , gen_encloser(qw(" "))          , greedy => 1 );
55Mdoc::def_macro( '.Dq' , gen_encloser(qw(\*[Lq] \*[Rq])), greedy => 1 );
56Mdoc::def_macro( '.Ql' , gen_encloser(qw(\[oq] \[cq]))  , greedy => 1 );
57Mdoc::def_macro( '.Sq' , gen_encloser(qw(\[oq] \[cq]))  , greedy => 1 );
58Mdoc::def_macro( '.Pq' , gen_encloser(qw/( )/)          , greedy => 1 );
59Mdoc::def_macro( '.D1' , sub { ".in +4\n", ns, @_ , ns , "\n.in -4" } , greedy => 1);
60
61Mdoc::def_macro( 'Oo',  sub { '[', @_ } );
62Mdoc::def_macro( 'Oc',  sub { ']', @_ } );
63
64Mdoc::def_macro( 'Po',  sub { '(', @_} );
65Mdoc::def_macro( 'Pc',  sub { ')', @_ } );
66
67Mdoc::def_macro( 'Bro', sub { '{', ns, @_ } );
68Mdoc::def_macro( 'Brc', sub { '}', @_ } );
69
70Mdoc::def_macro( '.Oo',  gen_encloser(qw([ ])), concat_until => '.Oc' );
71Mdoc::def_macro( '.Bro', gen_encloser(qw({ })), concat_until => '.Brc' );
72Mdoc::def_macro( '.Po',  gen_encloser(qw/( )/), concat_until => '.Pc' );
73
74Mdoc::def_macro( '.Ev', sub { @_ } );
75Mdoc::def_macro( '.An', sub { ".NOP ", @_, "\n.br" }, raw => 1 );
76Mdoc::def_macro( '.Li', sub { mapwords {"\\f[C]$_\\f[]"} @_ } );
77Mdoc::def_macro( '.Cm', sub { mapwords {"\\f\\*[B-Font]$_\\f[]"} @_ } );
78Mdoc::def_macro( '.Ic', sub { mapwords {"\\f\\*[B-Font]$_\\f[]"} @_ } );
79Mdoc::def_macro( '.Fl', sub { mapwords {"\\f\\*[B-Font]\\-$_\\f[]"} @_ } );
80Mdoc::def_macro( '.Ar', sub { mapwords {"\\f\\*[I-Font]$_\\f[]"} @_ } );
81Mdoc::def_macro( '.Em', sub { mapwords {"\\fI$_\\f[]"} @_ } );
82Mdoc::def_macro( '.Va', sub { mapwords {"\\fI$_\\f[]"} @_ } );
83Mdoc::def_macro( '.Sx', sub { mapwords {"\\fI$_\\f[]"} @_ } );
84Mdoc::def_macro( '.Xr', sub { "\\fC".(shift)."\\f[]\\fR(".(shift).")\\f[]", @_ } );
85Mdoc::def_macro( '.Fn', sub { "\\f\\*[B-Font]".(shift)."\\f[]\\fR()\\f[]" } );
86Mdoc::def_macro( '.Fn', sub { "\\fB".(shift)."\\f[]\\fR()\\f[]" } );
87Mdoc::def_macro( '.Fx', sub { "FreeBSD", @_ } );
88Mdoc::def_macro( '.Ux', sub { "UNIX", @_ } );
89
90Mdoc::def_macro( '.No', sub { ".NOP", map { ($_, ns) } @_ } );
91Mdoc::def_macro( '.Pa', sub { mapwords {"\\fI$_\\f[]"} @_; } );
92{
93    my $name;
94    Mdoc::def_macro('.Nm', sub {
95        $name = shift if (!$name);
96        "\\f\\*[B-Font]$name\\fP", @_
97    } );
98}
99
100########
101## lists
102########
103
104my %lists = (
105    bullet => sub {
106        Mdoc::def_macro('.It', sub { '.IP \fB\(bu\fP 2' });
107    },
108
109    column => sub {
110        Mdoc::def_macro('.It', sub { '.IP \fB\(bu\fP 2' });
111    },
112
113    tag    => sub {
114        my (%opts) = @_;
115
116        my $width = '';
117
118        if (exists $opts{width}) {
119            $width = ' '.((length $opts{width})+1);
120        }
121
122        if (exists $opts{compact}) {
123            my $dobrns = 0;
124            Mdoc::def_macro('.It', sub {
125                    my @ret = (".TP$width\n.NOP", hs);
126                    if ($dobrns) {
127                        ".br\n.ns\n", ns, @ret, @_;
128                    }
129                    else {
130                        $dobrns = 1;
131                        @ret, @_;
132                    }
133                }, raw => 1);
134        }
135        else {
136            Mdoc::def_macro('.It', sub {
137                    ".TP$width\n.NOP", hs, @_
138                }, raw => 1);
139        }
140    },
141);
142
143Mdoc::set_Bl_callback(do { my $nested = 0; sub {
144    my $type = shift;
145    my %opts = Mdoc::parse_opts(@_);
146    if (defined $type && $type =~ /-(\w+)/ && exists $lists{$1}) {
147
148        # Wrap nested lists with .RS and .RE
149        Mdoc::set_El_callback(sub { 
150                return '.RE' if $nested-- > 1;
151                return '.PP';
152            });
153
154        $lists{$1}->(%opts);
155
156        if ($nested++) {
157            return ".RS";
158        }
159        else {
160            return ();
161        }
162    }
163    else {
164        die "Invalid list type <$type>";
165    }
166}}, raw => 1);
167
168# don't bother with arguments for now and do what mdoc2man'.sh' did
169
170Mdoc::def_macro('.Bd', sub { ".br\n.in +4\n.nf" } );
171Mdoc::def_macro('.Ed', sub { ".in -4\n.fi" } );
172
173Mdoc::set_Re_callback(sub { 
174        my ($reference) = @_;
175        <<"REF";
176$reference->{authors},
177\\fI$reference->{title}\\fR,
178$reference->{optional}\n.PP
179REF
180});
181
182# Define all macros which have the same sub for inline and standalone macro
183for (qw(Xr Em Ar Fl Ic Cm Qq Op Nm Pa Sq Li Va Brq Pq Fx Ux)) {
184    my $m = Mdoc::get_macro(".$_");
185    Mdoc::def_macro($_, delete $m->{run}, %$m);
186}
187
188sub print_line {
189    print shift;
190    print "\n";
191}
192
193sub run {
194    print <<'DEFS';
195.de1 NOP
196.  it 1 an-trap
197.  if \\n[.$] \,\\$*\/
198..
199.ie t \
200.ds B-Font [CB]
201.ds I-Font [CI]
202.ds R-Font [CR]
203.el \
204.ds B-Font B
205.ds I-Font I
206.ds R-Font R
207DEFS
208
209    while (my ($macro, @args) = Mdoc::parse_line(\*STDIN, \&print_line)) {
210        my @ret = Mdoc::call_macro($macro, @args);
211        print_line(Mdoc::to_string(@ret)) if @ret;
212    }
213    return 0;
214}
215
216exit run(@ARGV) unless caller;
217
2181;
219__END__
220