1275970Scy#!/usr/bin/perl
2275970Scy
3275970Scy### ToDo
4275970Scy# Properly implement -columns in the "my %lists" definition...
5275970Scy#
6275970Scy# .Xr requires at least 1 arg, the code here expects at least 2
7275970Scy###
8275970Scy
9275970Scypackage mdoc2man;
10275970Scyuse strict;
11275970Scyuse warnings;
12275970Scyuse File::Basename;
13275970Scyuse lib dirname(__FILE__);
14275970Scyuse Mdoc qw(hs ns pp mapwords son soff stoggle gen_encloser);
15275970Scy
16275970Scy########
17275970Scy## Basic
18275970Scy########
19275970Scy
20275970ScyMdoc::def_macro( '.Sh', sub { '.SH', hs, @_ }, raw => 1);
21275970ScyMdoc::def_macro( '.Ss', sub { '.SS', hs, @_ }, raw => 1);
22275970ScyMdoc::def_macro( '.Pp', sub { ".sp \\n(Ppu\n.ne 2\n" } );
23275970ScyMdoc::def_macro( '.Nd', sub { "\\- @_" } );
24275970Scy
25275970Scy# Macros that enclose things
26275970ScyMdoc::def_macro( '.Brq', gen_encloser(qw({ }))          , greedy => 1 );
27275970ScyMdoc::def_macro( '.Op' , gen_encloser(qw([ ]))          , greedy => 1 );
28275970ScyMdoc::def_macro( '.Qq' , gen_encloser(qw(" "))          , greedy => 1 );
29275970ScyMdoc::def_macro( '.Dq' , gen_encloser(qw(\*[Lq] \*[Rq])), greedy => 1 );
30275970ScyMdoc::def_macro( '.Ql' , gen_encloser(qw(\[oq] \[cq]))  , greedy => 1 );
31275970ScyMdoc::def_macro( '.Sq' , gen_encloser(qw(\[oq] \[cq]))  , greedy => 1 );
32275970ScyMdoc::def_macro( '.Pq' , gen_encloser(qw/( )/)          , greedy => 1 );
33275970ScyMdoc::def_macro( '.D1' , sub { ".in +4\n", ns, @_ , ns , "\n.in -4" } , greedy => 1);
34275970Scy
35275970ScyMdoc::def_macro( 'Oo',  sub { '[', @_ } );
36275970ScyMdoc::def_macro( 'Oc',  sub { ']', @_ } );
37275970Scy
38275970ScyMdoc::def_macro( 'Po',  sub { '(', @_} );
39275970ScyMdoc::def_macro( 'Pc',  sub { ')', @_ } );
40275970Scy
41275970ScyMdoc::def_macro( 'Bro', sub { '{', ns, @_ } );
42275970ScyMdoc::def_macro( 'Brc', sub { '}', @_ } );
43275970Scy
44275970ScyMdoc::def_macro( '.Oo',  gen_encloser(qw([ ])), concat_until => '.Oc' );
45275970ScyMdoc::def_macro( '.Bro', gen_encloser(qw({ })), concat_until => '.Brc' );
46275970ScyMdoc::def_macro( '.Po',  gen_encloser(qw/( )/), concat_until => '.Pc' );
47275970Scy
48275970ScyMdoc::def_macro( '.Ev', sub { @_ } );
49275970ScyMdoc::def_macro( '.An', sub { ".NOP ", @_, "\n.br" }, raw => 1 );
50275970ScyMdoc::def_macro( '.Li', sub { mapwords {"\\f[C]$_\\f[]"} @_ } );
51275970ScyMdoc::def_macro( '.Cm', sub { mapwords {"\\f\\*[B-Font]$_\\f[]"} @_ } );
52275970ScyMdoc::def_macro( '.Ic', sub { mapwords {"\\f\\*[B-Font]$_\\f[]"} @_ } );
53275970ScyMdoc::def_macro( '.Fl', sub { mapwords {"\\f\\*[B-Font]\\-$_\\f[]"} @_ } );
54275970ScyMdoc::def_macro( '.Ar', sub { mapwords {"\\f\\*[I-Font]$_\\f[]"} @_ } );
55275970ScyMdoc::def_macro( '.Em', sub { mapwords {"\\fI$_\\f[]"} @_ } );
56275970ScyMdoc::def_macro( '.Va', sub { mapwords {"\\fI$_\\f[]"} @_ } );
57275970ScyMdoc::def_macro( '.Sx', sub { mapwords {"\\fI$_\\f[]"} @_ } );
58275970ScyMdoc::def_macro( '.Xr', sub { "\\fC".(shift)."\\fR(".(shift).")\\f[]", @_ } );
59275970ScyMdoc::def_macro( '.Fn', sub { "\\f\\*[B-Font]".(shift)."\\fR()\\f[]" } );
60275970ScyMdoc::def_macro( '.Fn', sub { "\\fB".(shift)."\\fR()\\f[]" } );
61275970ScyMdoc::def_macro( '.Fx', sub { "FreeBSD", @_ } );
62275970ScyMdoc::def_macro( '.Ux', sub { "UNIX", @_ } );
63275970Scy
64275970ScyMdoc::def_macro( '.No', sub { ".NOP", map { ($_, ns) } @_ } );
65275970ScyMdoc::def_macro( '.Pa', sub { mapwords {"\\fI$_\\f[]"} @_; } );
66275970Scy{
67275970Scy    my $name;
68275970Scy    Mdoc::def_macro('.Nm', sub {
69275970Scy        $name = shift if (!$name);
70275970Scy        "\\f\\*[B-Font]$name\\fP", @_
71275970Scy    } );
72275970Scy}
73275970Scy
74275970Scy########
75275970Scy## lists
76275970Scy########
77275970Scy
78275970Scymy %lists = (
79275970Scy    bullet => sub {
80275970Scy        Mdoc::def_macro('.It', sub { '.IP \fB\(bu\fP 2' });
81275970Scy    },
82275970Scy
83275970Scy    column => sub {
84275970Scy        Mdoc::def_macro('.It', sub { '.IP \fB\(bu\fP 2' });
85275970Scy    },
86275970Scy
87275970Scy    tag    => sub {
88275970Scy        my (%opts) = @_;
89275970Scy
90275970Scy        my $width = '';
91275970Scy
92275970Scy        if (exists $opts{width}) {
93275970Scy            $width = ' '.((length $opts{width})+1);
94275970Scy        }
95275970Scy
96275970Scy        if (exists $opts{compact}) {
97275970Scy            my $dobrns = 0;
98275970Scy            Mdoc::def_macro('.It', sub {
99275970Scy                    my @ret = (".TP$width\n.NOP", hs);
100275970Scy                    if ($dobrns) {
101275970Scy                        ".br\n.ns\n", ns, @ret, @_;
102275970Scy                    }
103275970Scy                    else {
104275970Scy                        $dobrns = 1;
105275970Scy                        @ret, @_;
106275970Scy                    }
107275970Scy                }, raw => 1);
108275970Scy        }
109275970Scy        else {
110275970Scy            Mdoc::def_macro('.It', sub {
111275970Scy                    ".TP$width\n.NOP", hs, @_
112275970Scy                }, raw => 1);
113275970Scy        }
114275970Scy    },
115275970Scy);
116275970Scy
117275970ScyMdoc::set_Bl_callback(do { my $nested = 0; sub {
118275970Scy    my $type = shift;
119275970Scy    my %opts = Mdoc::parse_opts(@_);
120275970Scy    if (defined $type && $type =~ /-(\w+)/ && exists $lists{$1}) {
121275970Scy
122275970Scy        # Wrap nested lists with .RS and .RE
123275970Scy        Mdoc::set_El_callback(sub { 
124275970Scy                return '.RE' if $nested-- > 1;
125275970Scy                return '.PP';
126275970Scy            });
127275970Scy
128275970Scy        $lists{$1}->(%opts);
129275970Scy
130275970Scy        if ($nested++) {
131275970Scy            return ".RS";
132275970Scy        }
133275970Scy        else {
134275970Scy            return ();
135275970Scy        }
136275970Scy    }
137275970Scy    else {
138275970Scy        die "Invalid list type <$type>";
139275970Scy    }
140275970Scy}}, raw => 1);
141275970Scy
142275970Scy# don't bother with arguments for now and do what mdoc2man'.sh' did
143275970Scy
144275970ScyMdoc::def_macro('.Bd', sub { ".br\n.in +4\n.nf" } );
145275970ScyMdoc::def_macro('.Ed', sub { ".in -4\n.fi" } );
146275970Scy
147275970ScyMdoc::set_Re_callback(sub { 
148275970Scy        my ($reference) = @_;
149275970Scy        <<"REF";
150275970Scy$reference->{authors},
151275970Scy\\fI$reference->{title}\\fR,
152275970Scy$reference->{optional}\n.PP
153275970ScyREF
154275970Scy});
155275970Scy
156275970Scy# Define all macros which have the same sub for inline and standalone macro
157275970Scyfor (qw(Xr Em Ar Fl Ic Cm Qq Op Nm Pa Sq Li Va Brq Pq Fx Ux)) {
158275970Scy    my $m = Mdoc::get_macro(".$_");
159275970Scy    Mdoc::def_macro($_, delete $m->{run}, %$m);
160275970Scy}
161275970Scy
162275970Scysub print_line {
163275970Scy    print shift;
164275970Scy    print "\n";
165275970Scy}
166275970Scy
167275970Scysub run {
168275970Scy    print <<'DEFS';
169275970Scy.de1 NOP
170275970Scy.  it 1 an-trap
171275970Scy.  if \\n[.$] \,\\$*\/
172275970Scy..
173275970Scy.ie t \
174275970Scy.ds B-Font [CB]
175275970Scy.ds I-Font [CI]
176275970Scy.ds R-Font [CR]
177275970Scy.el \
178275970Scy.ds B-Font B
179275970Scy.ds I-Font I
180275970Scy.ds R-Font R
181275970ScyDEFS
182275970Scy
183275970Scy    while (my ($macro, @args) = Mdoc::parse_line(\*STDIN, \&print_line)) {
184275970Scy        my @ret = Mdoc::call_macro($macro, @args);
185275970Scy        print_line(Mdoc::to_string(@ret)) if @ret;
186275970Scy    }
187275970Scy    return 0;
188275970Scy}
189275970Scy
190275970Scyexit run(@ARGV) unless caller;
191275970Scy
192275970Scy1;
193275970Scy__END__
194