1284990Scy#! /usr/local/bin/perl
2284990Scy
3284990Scy## mdoc2man.pl -- Convert mdoc tags to man tags
4284990Scy##
5284990Scy## Author:	Harlan Stenn <stenn@ntp.org>
6284990Scy##		
7284990Scy##
8284990Scy##  This file is part of AutoOpts, a companion to AutoGen.
9284990Scy##  AutoOpts is free software.
10284990Scy##  AutoOpts is Copyright (C) 1992-2015 by Bruce Korb - all rights reserved
11284990Scy##
12284990Scy##  AutoOpts is available under any one of two licenses.  The license
13284990Scy##  in use must be one of these two and the choice is under the control
14284990Scy##  of the user of the license.
15284990Scy##
16284990Scy##   The GNU Lesser General Public License, version 3 or later
17284990Scy##      See the files "COPYING.lgplv3" and "COPYING.gplv3"
18284990Scy##
19284990Scy##   The Modified Berkeley Software Distribution License
20284990Scy##      See the file "COPYING.mbsd"
21284990Scy##
22284990Scy##  These files have the following sha256 sums:
23284990Scy##
24284990Scy##  8584710e9b04216a394078dc156b781d0b47e1729104d666658aecef8ee32e95  COPYING.gplv3
25284990Scy##  4379e7444a0e2ce2b12dd6f5a52a27a4d02d39d247901d3285c88cf0d37f477b  COPYING.lgplv3
26284990Scy##  13aa749a5b0a454917a944ed8fffc530b784f5ead522b1aacaf4ec8aa55a6239  COPYING.mbsd
27284990Scy
28284990Scy### ToDo
29284990Scy# Properly implement -columns in the "my %lists" definition...
30284990Scy#
31284990Scy# .Xr requires at least 1 arg, the code here expects at least 2
32284990Scy#
33284990Scy###
34284990Scy
35284990Scypackage mdoc2man;
36284990Scyuse strict;
37284990Scyuse warnings;
38284990Scyuse File::Basename;
39284990Scyuse lib dirname(__FILE__);
40284990Scyuse Mdoc qw(hs ns pp mapwords son soff stoggle gen_encloser);
41284990Scy
42284990Scy########
43284990Scy## Basic
44284990Scy########
45284990Scy
46284990ScyMdoc::def_macro( '.Sh', sub { '.SH', hs, @_ }, raw => 1);
47284990ScyMdoc::def_macro( '.Ss', sub { '.SS', hs, @_ }, raw => 1);
48284990ScyMdoc::def_macro( '.Pp', sub { ".sp \\n(Ppu\n.ne 2\n" } );
49284990ScyMdoc::def_macro( '.Nd', sub { "\\- @_" } );
50284990Scy
51284990Scy# Macros that enclose things
52284990ScyMdoc::def_macro( '.Brq', gen_encloser(qw({ }))          , greedy => 1 );
53284990ScyMdoc::def_macro( '.Op' , gen_encloser(qw([ ]))          , greedy => 1 );
54284990ScyMdoc::def_macro( '.Qq' , gen_encloser(qw(" "))          , greedy => 1 );
55284990ScyMdoc::def_macro( '.Dq' , gen_encloser(qw(\*[Lq] \*[Rq])), greedy => 1 );
56284990ScyMdoc::def_macro( '.Ql' , gen_encloser(qw(\[oq] \[cq]))  , greedy => 1 );
57284990ScyMdoc::def_macro( '.Sq' , gen_encloser(qw(\[oq] \[cq]))  , greedy => 1 );
58284990ScyMdoc::def_macro( '.Pq' , gen_encloser(qw/( )/)          , greedy => 1 );
59284990ScyMdoc::def_macro( '.D1' , sub { ".in +4\n", ns, @_ , ns , "\n.in -4" } , greedy => 1);
60284990Scy
61284990ScyMdoc::def_macro( 'Oo',  sub { '[', @_ } );
62284990ScyMdoc::def_macro( 'Oc',  sub { ']', @_ } );
63284990Scy
64284990ScyMdoc::def_macro( 'Po',  sub { '(', @_} );
65284990ScyMdoc::def_macro( 'Pc',  sub { ')', @_ } );
66284990Scy
67284990ScyMdoc::def_macro( 'Bro', sub { '{', ns, @_ } );
68284990ScyMdoc::def_macro( 'Brc', sub { '}', @_ } );
69284990Scy
70284990ScyMdoc::def_macro( '.Oo',  gen_encloser(qw([ ])), concat_until => '.Oc' );
71284990ScyMdoc::def_macro( '.Bro', gen_encloser(qw({ })), concat_until => '.Brc' );
72284990ScyMdoc::def_macro( '.Po',  gen_encloser(qw/( )/), concat_until => '.Pc' );
73284990Scy
74284990ScyMdoc::def_macro( '.Ev', sub { @_ } );
75284990ScyMdoc::def_macro( '.An', sub { ".NOP ", @_, "\n.br" }, raw => 1 );
76284990ScyMdoc::def_macro( '.Li', sub { mapwords {"\\f[C]$_\\f[]"} @_ } );
77284990ScyMdoc::def_macro( '.Cm', sub { mapwords {"\\f\\*[B-Font]$_\\f[]"} @_ } );
78284990ScyMdoc::def_macro( '.Ic', sub { mapwords {"\\f\\*[B-Font]$_\\f[]"} @_ } );
79284990ScyMdoc::def_macro( '.Fl', sub { mapwords {"\\f\\*[B-Font]\\-$_\\f[]"} @_ } );
80284990ScyMdoc::def_macro( '.Ar', sub { mapwords {"\\f\\*[I-Font]$_\\f[]"} @_ } );
81284990ScyMdoc::def_macro( '.Em', sub { mapwords {"\\fI$_\\f[]"} @_ } );
82284990ScyMdoc::def_macro( '.Va', sub { mapwords {"\\fI$_\\f[]"} @_ } );
83284990ScyMdoc::def_macro( '.Sx', sub { mapwords {"\\fI$_\\f[]"} @_ } );
84284990ScyMdoc::def_macro( '.Xr', sub { "\\fC".(shift)."\\f[]\\fR(".(shift).")\\f[]", @_ } );
85284990ScyMdoc::def_macro( '.Fn', sub { "\\f\\*[B-Font]".(shift)."\\f[]\\fR()\\f[]" } );
86284990ScyMdoc::def_macro( '.Fn', sub { "\\fB".(shift)."\\f[]\\fR()\\f[]" } );
87284990ScyMdoc::def_macro( '.Fx', sub { "FreeBSD", @_ } );
88284990ScyMdoc::def_macro( '.Ux', sub { "UNIX", @_ } );
89284990Scy
90284990ScyMdoc::def_macro( '.No', sub { ".NOP", map { ($_, ns) } @_ } );
91284990ScyMdoc::def_macro( '.Pa', sub { mapwords {"\\fI$_\\f[]"} @_; } );
92284990Scy{
93284990Scy    my $name;
94284990Scy    Mdoc::def_macro('.Nm', sub {
95284990Scy        $name = shift if (!$name);
96284990Scy        "\\f\\*[B-Font]$name\\fP", @_
97284990Scy    } );
98284990Scy}
99284990Scy
100284990Scy########
101284990Scy## lists
102284990Scy########
103284990Scy
104284990Scymy %lists = (
105284990Scy    bullet => sub {
106284990Scy        Mdoc::def_macro('.It', sub { '.IP \fB\(bu\fP 2' });
107284990Scy    },
108284990Scy
109284990Scy    column => sub {
110284990Scy        Mdoc::def_macro('.It', sub { '.IP \fB\(bu\fP 2' });
111284990Scy    },
112284990Scy
113284990Scy    tag    => sub {
114284990Scy        my (%opts) = @_;
115284990Scy
116284990Scy        my $width = '';
117284990Scy
118284990Scy        if (exists $opts{width}) {
119284990Scy            $width = ' '.((length $opts{width})+1);
120284990Scy        }
121284990Scy
122284990Scy        if (exists $opts{compact}) {
123284990Scy            my $dobrns = 0;
124284990Scy            Mdoc::def_macro('.It', sub {
125284990Scy                    my @ret = (".TP$width\n.NOP", hs);
126284990Scy                    if ($dobrns) {
127284990Scy                        ".br\n.ns\n", ns, @ret, @_;
128284990Scy                    }
129284990Scy                    else {
130284990Scy                        $dobrns = 1;
131284990Scy                        @ret, @_;
132284990Scy                    }
133284990Scy                }, raw => 1);
134284990Scy        }
135284990Scy        else {
136284990Scy            Mdoc::def_macro('.It', sub {
137284990Scy                    ".TP$width\n.NOP", hs, @_
138284990Scy                }, raw => 1);
139284990Scy        }
140284990Scy    },
141284990Scy);
142284990Scy
143284990ScyMdoc::set_Bl_callback(do { my $nested = 0; sub {
144284990Scy    my $type = shift;
145284990Scy    my %opts = Mdoc::parse_opts(@_);
146284990Scy    if (defined $type && $type =~ /-(\w+)/ && exists $lists{$1}) {
147284990Scy
148284990Scy        # Wrap nested lists with .RS and .RE
149284990Scy        Mdoc::set_El_callback(sub { 
150284990Scy                return '.RE' if $nested-- > 1;
151284990Scy                return '.PP';
152284990Scy            });
153284990Scy
154284990Scy        $lists{$1}->(%opts);
155284990Scy
156284990Scy        if ($nested++) {
157284990Scy            return ".RS";
158284990Scy        }
159284990Scy        else {
160284990Scy            return ();
161284990Scy        }
162284990Scy    }
163284990Scy    else {
164284990Scy        die "Invalid list type <$type>";
165284990Scy    }
166284990Scy}}, raw => 1);
167284990Scy
168284990Scy# don't bother with arguments for now and do what mdoc2man'.sh' did
169284990Scy
170284990ScyMdoc::def_macro('.Bd', sub { ".br\n.in +4\n.nf" } );
171284990ScyMdoc::def_macro('.Ed', sub { ".in -4\n.fi" } );
172284990Scy
173284990ScyMdoc::set_Re_callback(sub { 
174284990Scy        my ($reference) = @_;
175284990Scy        <<"REF";
176284990Scy$reference->{authors},
177284990Scy\\fI$reference->{title}\\fR,
178284990Scy$reference->{optional}\n.PP
179284990ScyREF
180284990Scy});
181284990Scy
182284990Scy# Define all macros which have the same sub for inline and standalone macro
183284990Scyfor (qw(Xr Em Ar Fl Ic Cm Qq Op Nm Pa Sq Li Va Brq Pq Fx Ux)) {
184284990Scy    my $m = Mdoc::get_macro(".$_");
185284990Scy    Mdoc::def_macro($_, delete $m->{run}, %$m);
186284990Scy}
187284990Scy
188284990Scysub print_line {
189284990Scy    print shift;
190284990Scy    print "\n";
191284990Scy}
192284990Scy
193284990Scysub run {
194284990Scy    print <<'DEFS';
195284990Scy.de1 NOP
196284990Scy.  it 1 an-trap
197284990Scy.  if \\n[.$] \,\\$*\/
198284990Scy..
199284990Scy.ie t \
200284990Scy.ds B-Font [CB]
201284990Scy.ds I-Font [CI]
202284990Scy.ds R-Font [CR]
203284990Scy.el \
204284990Scy.ds B-Font B
205284990Scy.ds I-Font I
206284990Scy.ds R-Font R
207284990ScyDEFS
208284990Scy
209284990Scy    while (my ($macro, @args) = Mdoc::parse_line(\*STDIN, \&print_line)) {
210284990Scy        my @ret = Mdoc::call_macro($macro, @args);
211284990Scy        print_line(Mdoc::to_string(@ret)) if @ret;
212284990Scy    }
213284990Scy    return 0;
214284990Scy}
215284990Scy
216284990Scyexit run(@ARGV) unless caller;
217284990Scy
218284990Scy1;
219284990Scy__END__
220