1*1b6f2cd4Schristos#!/usr/bin/perl
2*1b6f2cd4Schristos
3*1b6f2cd4Schristos### ToDo
4*1b6f2cd4Schristos# Properly implement -columns in the "my %lists" definition...
5*1b6f2cd4Schristos#
6*1b6f2cd4Schristos# .Xr requires at least 1 arg, the code here expects at least 2
7*1b6f2cd4Schristos###
8*1b6f2cd4Schristos
9*1b6f2cd4Schristospackage mdoc2man;
10*1b6f2cd4Schristosuse strict;
11*1b6f2cd4Schristosuse warnings;
12*1b6f2cd4Schristosuse File::Basename;
13*1b6f2cd4Schristosuse lib dirname(__FILE__);
14*1b6f2cd4Schristosuse Mdoc qw(hs ns pp mapwords son soff stoggle gen_encloser);
15*1b6f2cd4Schristos
16*1b6f2cd4Schristos########
17*1b6f2cd4Schristos## Basic
18*1b6f2cd4Schristos########
19*1b6f2cd4Schristos
20*1b6f2cd4SchristosMdoc::def_macro( '.Sh', sub { '.SH', hs, @_ }, raw => 1);
21*1b6f2cd4SchristosMdoc::def_macro( '.Ss', sub { '.SS', hs, @_ }, raw => 1);
22*1b6f2cd4SchristosMdoc::def_macro( '.Pp', sub { ".sp \\n(Ppu\n.ne 2\n" } );
23*1b6f2cd4SchristosMdoc::def_macro( '.Nd', sub { "\\- @_" } );
24*1b6f2cd4Schristos
25*1b6f2cd4Schristos# Macros that enclose things
26*1b6f2cd4SchristosMdoc::def_macro( '.Brq', gen_encloser(qw({ }))          , greedy => 1 );
27*1b6f2cd4SchristosMdoc::def_macro( '.Op' , gen_encloser(qw([ ]))          , greedy => 1 );
28*1b6f2cd4SchristosMdoc::def_macro( '.Qq' , gen_encloser(qw(" "))          , greedy => 1 );
29*1b6f2cd4SchristosMdoc::def_macro( '.Dq' , gen_encloser(qw(\*[Lq] \*[Rq])), greedy => 1 );
30*1b6f2cd4SchristosMdoc::def_macro( '.Ql' , gen_encloser(qw(\[oq] \[cq]))  , greedy => 1 );
31*1b6f2cd4SchristosMdoc::def_macro( '.Sq' , gen_encloser(qw(\[oq] \[cq]))  , greedy => 1 );
32*1b6f2cd4SchristosMdoc::def_macro( '.Pq' , gen_encloser(qw/( )/)          , greedy => 1 );
33*1b6f2cd4SchristosMdoc::def_macro( '.D1' , sub { ".in +4\n", ns, @_ , ns , "\n.in -4" } , greedy => 1);
34*1b6f2cd4Schristos
35*1b6f2cd4SchristosMdoc::def_macro( 'Oo',  sub { '[', @_ } );
36*1b6f2cd4SchristosMdoc::def_macro( 'Oc',  sub { ']', @_ } );
37*1b6f2cd4Schristos
38*1b6f2cd4SchristosMdoc::def_macro( 'Po',  sub { '(', @_} );
39*1b6f2cd4SchristosMdoc::def_macro( 'Pc',  sub { ')', @_ } );
40*1b6f2cd4Schristos
41*1b6f2cd4SchristosMdoc::def_macro( 'Bro', sub { '{', ns, @_ } );
42*1b6f2cd4SchristosMdoc::def_macro( 'Brc', sub { '}', @_ } );
43*1b6f2cd4Schristos
44*1b6f2cd4SchristosMdoc::def_macro( '.Oo',  gen_encloser(qw([ ])), concat_until => '.Oc' );
45*1b6f2cd4SchristosMdoc::def_macro( '.Bro', gen_encloser(qw({ })), concat_until => '.Brc' );
46*1b6f2cd4SchristosMdoc::def_macro( '.Po',  gen_encloser(qw/( )/), concat_until => '.Pc' );
47*1b6f2cd4Schristos
48*1b6f2cd4SchristosMdoc::def_macro( '.Ev', sub { @_ } );
49*1b6f2cd4SchristosMdoc::def_macro( '.An', sub { ".NOP ", @_, "\n.br" }, raw => 1 );
50*1b6f2cd4SchristosMdoc::def_macro( '.Li', sub { mapwords {"\\f[C]$_\\f[]"} @_ } );
51*1b6f2cd4SchristosMdoc::def_macro( '.Cm', sub { mapwords {"\\f\\*[B-Font]$_\\f[]"} @_ } );
52*1b6f2cd4SchristosMdoc::def_macro( '.Ic', sub { mapwords {"\\f\\*[B-Font]$_\\f[]"} @_ } );
53*1b6f2cd4SchristosMdoc::def_macro( '.Fl', sub { mapwords {"\\f\\*[B-Font]\\-$_\\f[]"} @_ } );
54*1b6f2cd4SchristosMdoc::def_macro( '.Ar', sub { mapwords {"\\f\\*[I-Font]$_\\f[]"} @_ } );
55*1b6f2cd4SchristosMdoc::def_macro( '.Em', sub { mapwords {"\\fI$_\\f[]"} @_ } );
56*1b6f2cd4SchristosMdoc::def_macro( '.Va', sub { mapwords {"\\fI$_\\f[]"} @_ } );
57*1b6f2cd4SchristosMdoc::def_macro( '.Sx', sub { mapwords {"\\fI$_\\f[]"} @_ } );
58*1b6f2cd4SchristosMdoc::def_macro( '.Xr', sub { "\\fC".(shift)."\\fR(".(shift).")\\f[]", @_ } );
59*1b6f2cd4SchristosMdoc::def_macro( '.Fn', sub { "\\f\\*[B-Font]".(shift)."\\fR()\\f[]" } );
60*1b6f2cd4SchristosMdoc::def_macro( '.Fn', sub { "\\fB".(shift)."\\fR()\\f[]" } );
61*1b6f2cd4SchristosMdoc::def_macro( '.Fx', sub { "FreeBSD", @_ } );
62*1b6f2cd4SchristosMdoc::def_macro( '.Ux', sub { "UNIX", @_ } );
63*1b6f2cd4Schristos
64*1b6f2cd4SchristosMdoc::def_macro( '.No', sub { ".NOP", map { ($_, ns) } @_ } );
65*1b6f2cd4SchristosMdoc::def_macro( '.Pa', sub { mapwords {"\\fI$_\\f[]"} @_; } );
66*1b6f2cd4Schristos{
67*1b6f2cd4Schristos    my $name;
68*1b6f2cd4Schristos    Mdoc::def_macro('.Nm', sub {
69*1b6f2cd4Schristos        $name = shift if (!$name);
70*1b6f2cd4Schristos        "\\f\\*[B-Font]$name\\fP", @_
71*1b6f2cd4Schristos    } );
72*1b6f2cd4Schristos}
73*1b6f2cd4Schristos
74*1b6f2cd4Schristos########
75*1b6f2cd4Schristos## lists
76*1b6f2cd4Schristos########
77*1b6f2cd4Schristos
78*1b6f2cd4Schristosmy %lists = (
79*1b6f2cd4Schristos    bullet => sub {
80*1b6f2cd4Schristos        Mdoc::def_macro('.It', sub { '.IP \fB\(bu\fP 2' });
81*1b6f2cd4Schristos    },
82*1b6f2cd4Schristos
83*1b6f2cd4Schristos    column => sub {
84*1b6f2cd4Schristos        Mdoc::def_macro('.It', sub { '.IP \fB\(bu\fP 2' });
85*1b6f2cd4Schristos    },
86*1b6f2cd4Schristos
87*1b6f2cd4Schristos    tag    => sub {
88*1b6f2cd4Schristos        my (%opts) = @_;
89*1b6f2cd4Schristos
90*1b6f2cd4Schristos        my $width = '';
91*1b6f2cd4Schristos
92*1b6f2cd4Schristos        if (exists $opts{width}) {
93*1b6f2cd4Schristos            $width = ' '.((length $opts{width})+1);
94*1b6f2cd4Schristos        }
95*1b6f2cd4Schristos
96*1b6f2cd4Schristos        if (exists $opts{compact}) {
97*1b6f2cd4Schristos            my $dobrns = 0;
98*1b6f2cd4Schristos            Mdoc::def_macro('.It', sub {
99*1b6f2cd4Schristos                    my @ret = (".TP$width\n.NOP", hs);
100*1b6f2cd4Schristos                    if ($dobrns) {
101*1b6f2cd4Schristos                        ".br\n.ns\n", ns, @ret, @_;
102*1b6f2cd4Schristos                    }
103*1b6f2cd4Schristos                    else {
104*1b6f2cd4Schristos                        $dobrns = 1;
105*1b6f2cd4Schristos                        @ret, @_;
106*1b6f2cd4Schristos                    }
107*1b6f2cd4Schristos                }, raw => 1);
108*1b6f2cd4Schristos        }
109*1b6f2cd4Schristos        else {
110*1b6f2cd4Schristos            Mdoc::def_macro('.It', sub {
111*1b6f2cd4Schristos                    ".TP$width\n.NOP", hs, @_
112*1b6f2cd4Schristos                }, raw => 1);
113*1b6f2cd4Schristos        }
114*1b6f2cd4Schristos    },
115*1b6f2cd4Schristos);
116*1b6f2cd4Schristos
117*1b6f2cd4SchristosMdoc::set_Bl_callback(do { my $nested = 0; sub {
118*1b6f2cd4Schristos    my $type = shift;
119*1b6f2cd4Schristos    my %opts = Mdoc::parse_opts(@_);
120*1b6f2cd4Schristos    if (defined $type && $type =~ /-(\w+)/ && exists $lists{$1}) {
121*1b6f2cd4Schristos
122*1b6f2cd4Schristos        # Wrap nested lists with .RS and .RE
123*1b6f2cd4Schristos        Mdoc::set_El_callback(sub {
124*1b6f2cd4Schristos                return '.RE' if $nested-- > 1;
125*1b6f2cd4Schristos                return '.PP';
126*1b6f2cd4Schristos            });
127*1b6f2cd4Schristos
128*1b6f2cd4Schristos        $lists{$1}->(%opts);
129*1b6f2cd4Schristos
130*1b6f2cd4Schristos        if ($nested++) {
131*1b6f2cd4Schristos            return ".RS";
132*1b6f2cd4Schristos        }
133*1b6f2cd4Schristos        else {
134*1b6f2cd4Schristos            return ();
135*1b6f2cd4Schristos        }
136*1b6f2cd4Schristos    }
137*1b6f2cd4Schristos    else {
138*1b6f2cd4Schristos        die "Invalid list type <$type>";
139*1b6f2cd4Schristos    }
140*1b6f2cd4Schristos}}, raw => 1);
141*1b6f2cd4Schristos
142*1b6f2cd4Schristos# don't bother with arguments for now and do what mdoc2man'.sh' did
143*1b6f2cd4Schristos
144*1b6f2cd4SchristosMdoc::def_macro('.Bd', sub { ".br\n.in +4\n.nf" } );
145*1b6f2cd4SchristosMdoc::def_macro('.Ed', sub { ".in -4\n.fi" } );
146*1b6f2cd4Schristos
147*1b6f2cd4SchristosMdoc::set_Re_callback(sub {
148*1b6f2cd4Schristos        my ($reference) = @_;
149*1b6f2cd4Schristos        <<"REF";
150*1b6f2cd4Schristos$reference->{authors},
151*1b6f2cd4Schristos\\fI$reference->{title}\\fR,
152*1b6f2cd4Schristos$reference->{optional}\n.PP
153*1b6f2cd4SchristosREF
154*1b6f2cd4Schristos});
155*1b6f2cd4Schristos
156*1b6f2cd4Schristos# Define all macros which have the same sub for inline and standalone macro
157*1b6f2cd4Schristosfor (qw(Xr Em Ar Fl Ic Cm Qq Op Nm Pa Sq Li Va Brq Pq Fx Ux)) {
158*1b6f2cd4Schristos    my $m = Mdoc::get_macro(".$_");
159*1b6f2cd4Schristos    Mdoc::def_macro($_, delete $m->{run}, %$m);
160*1b6f2cd4Schristos}
161*1b6f2cd4Schristos
162*1b6f2cd4Schristossub print_line {
163*1b6f2cd4Schristos    print shift;
164*1b6f2cd4Schristos    print "\n";
165*1b6f2cd4Schristos}
166*1b6f2cd4Schristos
167*1b6f2cd4Schristossub run {
168*1b6f2cd4Schristos    print <<'DEFS';
169*1b6f2cd4Schristos.de1 NOP
170*1b6f2cd4Schristos.  it 1 an-trap
171*1b6f2cd4Schristos.  if \\n[.$] \,\\$*\/
172*1b6f2cd4Schristos..
173*1b6f2cd4Schristos.ie t \
174*1b6f2cd4Schristos.ds B-Font [CB]
175*1b6f2cd4Schristos.ds I-Font [CI]
176*1b6f2cd4Schristos.ds R-Font [CR]
177*1b6f2cd4Schristos.el \
178*1b6f2cd4Schristos.ds B-Font B
179*1b6f2cd4Schristos.ds I-Font I
180*1b6f2cd4Schristos.ds R-Font R
181*1b6f2cd4SchristosDEFS
182*1b6f2cd4Schristos
183*1b6f2cd4Schristos    while (my ($macro, @args) = Mdoc::parse_line(\*STDIN, \&print_line)) {
184*1b6f2cd4Schristos        my @ret = Mdoc::call_macro($macro, @args);
185*1b6f2cd4Schristos        print_line(Mdoc::to_string(@ret)) if @ret;
186*1b6f2cd4Schristos    }
187*1b6f2cd4Schristos    return 0;
188*1b6f2cd4Schristos}
189*1b6f2cd4Schristos
190*1b6f2cd4Schristosexit run(@ARGV) unless caller;
191*1b6f2cd4Schristos
192*1b6f2cd4Schristos1;
193*1b6f2cd4Schristos__END__
194