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