1 1.1 christos #! /usr/bin/perl 2 1.1 christos 3 1.1 christos ### To Do: 4 1.1 christos 5 1.1 christos # the Bl -column command needs work: 6 1.1 christos # - support for "-offset" 7 1.1 christos # - support for the header widths 8 1.1 christos 9 1.1 christos # 10 1.1 christos 11 1.1 christos ### 12 1.1 christos 13 1.1 christos package mdoc2texi; 14 1.1 christos use strict; 15 1.1 christos use warnings; 16 1.1 christos use File::Basename qw(dirname); 17 1.1 christos use lib dirname(__FILE__); 18 1.1 christos use Mdoc qw(ns pp hs mapwords gen_encloser nl); 19 1.1 christos 20 1.1 christos # Ignore commments 21 1.1 christos Mdoc::def_macro( '.\"', sub { () } ); 22 1.1 christos 23 1.1 christos # Enclosers 24 1.1 christos Mdoc::def_macro( '.An', sub { @_, ns, '@*' } ); 25 1.1 christos Mdoc::def_macro( '.Aq', gen_encloser(qw(< >)), greedy => 1); 26 1.1 christos Mdoc::def_macro( '.Bq', gen_encloser(qw([ ])), greedy => 1); 27 1.1 christos Mdoc::def_macro( '.Brq', gen_encloser(qw(@{ @})), greedy => 1); 28 1.1 christos Mdoc::def_macro( '.Pq', gen_encloser(qw/( )/), greedy => 1); 29 1.1 christos Mdoc::def_macro( '.Qq', gen_encloser(qw(" ")), greedy => 1); 30 1.1 christos Mdoc::def_macro( '.Op', gen_encloser(qw(@code{[ ]})), greedy => 1); 31 1.1 christos Mdoc::def_macro( '.Ql', gen_encloser(qw(@quoteleft{} @quoteright{})), 32 1.1 christos greedy => 1); 33 1.1 christos Mdoc::def_macro( '.Sq', gen_encloser(qw(@quoteleft{} @quoteright{})), 34 1.1 christos greedy => 1); 35 1.1 christos Mdoc::def_macro( '.Dq', gen_encloser(qw(@quotedblleft{} @quotedblright{})), 36 1.1 christos greedy => 1); 37 1.1 christos Mdoc::def_macro( '.Eq', sub { 38 1.1 christos my ($o, $c) = (shift, pop); 39 1.1 christos gen_encloser($o, $c)->(@_) 40 1.1 christos }, greedy => 1); 41 1.1 christos Mdoc::def_macro( '.D1', sub { "\@example\n", ns, @_, ns, "\n\@end example" }, 42 1.1 christos greedy => 1); 43 1.1 christos Mdoc::def_macro( '.Dl', sub { "\@example\n", ns, @_, ns, "\n\@end example" }, 44 1.1 christos greedy => 1); 45 1.1 christos 46 1.1 christos Mdoc::def_macro( '.Oo', gen_encloser(qw(@code{[ ]})), concat_until => '.Oc'); 47 1.1 christos Mdoc::def_macro( 'Oo', sub { '@code{[', ns, @_ } ); 48 1.1 christos Mdoc::def_macro( 'Oc', sub { @_, ns, pp(']}') } ); 49 1.1 christos 50 1.1 christos Mdoc::def_macro( '.Bro', gen_encloser(qw(@code{@{ @}})), concat_until => '.Brc'); 51 1.1 christos Mdoc::def_macro( 'Bro', sub { '@code{@{', ns, @_ } ); 52 1.1 christos Mdoc::def_macro( 'Brc', sub { @_, ns, pp('@}}') } ); 53 1.1 christos 54 1.1 christos Mdoc::def_macro( '.Po', gen_encloser(qw/( )/), concat_until => '.Pc'); 55 1.1 christos Mdoc::def_macro( 'Po', sub { '(', @_ } ); 56 1.1 christos Mdoc::def_macro( 'Pc', sub { @_, ')' } ); 57 1.1 christos 58 1.1 christos Mdoc::def_macro( '.Ar', sub { mapwords {"\@kbd{$_}"} @_ } ); 59 1.1 christos Mdoc::def_macro( '.Fl', sub { mapwords {"\@code{-$_}"} @_ } ); 60 1.1 christos Mdoc::def_macro( '.Cm', sub { mapwords {"\@code{-$_}"} @_ } ); 61 1.1 christos Mdoc::def_macro( '.Ic', sub { mapwords {"\@code{$_}"} @_ } ); 62 1.1 christos Mdoc::def_macro( '.Cm', sub { mapwords {"\@code{$_}"} @_ } ); 63 1.1 christos Mdoc::def_macro( '.Li', sub { mapwords {"\@code{$_}"} @_ } ); 64 1.1 christos Mdoc::def_macro( '.Va', sub { mapwords {"\@code{$_}"} @_ } ); 65 1.1 christos Mdoc::def_macro( '.Em', sub { mapwords {"\@emph{$_}"} @_ } ); 66 1.1 christos Mdoc::def_macro( '.Fn', sub { '@code{'.(shift).'()}' } ); 67 1.1 christos Mdoc::def_macro( '.Ss', sub { "\@subsubsection", hs, @_ }); 68 1.1 christos Mdoc::def_macro( '.Sh', sub { 69 1.1 christos my $name = "@_"; 70 1.1 christos "\@node", hs, "$name\n", ns, "\@subsection", hs, $name 71 1.1 christos }); 72 1.1 christos Mdoc::def_macro( '.Ss', sub { "\@subsubsection", hs, @_ }); 73 1.1 christos Mdoc::def_macro( '.Xr', sub { '@code{'.(shift).'('.(shift).')}', @_ } ); 74 1.1 christos Mdoc::def_macro( '.Sx', gen_encloser(qw(@ref{ })) ); 75 1.1 christos Mdoc::def_macro( '.Ux', sub { '@sc{unix}', @_ } ); 76 1.1 christos Mdoc::def_macro( '.Fx', sub { '@sc{freebsd}', @_ } ); 77 1.1 christos { 78 1.1 christos my $name; 79 1.1 christos Mdoc::def_macro('.Nm', sub { 80 1.1 christos $name = shift || $ENV{AG_DEF_PROG_NAME} || 'XXX' if (!$name); 81 1.1 christos "\@code{$name}" 82 1.1 christos } ); 83 1.1 christos } 84 1.1 christos Mdoc::def_macro( '.Pa', sub { mapwords {"\@file{$_}"} @_ } ); 85 1.1 christos Mdoc::def_macro( '.Pp', sub { '' } ); 86 1.1 christos 87 1.1 christos # Setup references 88 1.1 christos 89 1.1 christos Mdoc::def_macro( '.Rs', sub { "\@*\n", @_ } ); 90 1.1 christos Mdoc::set_Re_callback(sub { 91 1.1 christos my ($reference) = @_; 92 1.1 christos "@*\n", ns, $reference->{authors}, ',', "\@emph{$reference->{title}}", 93 1.1 christos ',', $reference->{optional} 94 1.1 christos }); 95 1.1 christos 96 1.1 christos # Set up Bd/Ed 97 1.1 christos 98 1.1 christos my %displays = ( 99 1.1 christos literal => [ '@verbatim', '@end verbatim' ], 100 1.1 christos ); 101 1.1 christos 102 1.1 christos Mdoc::def_macro( '.Bd', sub { 103 1.1 christos (my $type = shift) =~ s/^-//; 104 1.1 christos die "Not supported display type <$type>" 105 1.1 christos if not exists $displays{ $type }; 106 1.1 christos 107 1.1 christos my $orig_ed = Mdoc::get_macro('.Ed'); 108 1.1 christos Mdoc::def_macro('.Ed', sub { 109 1.1 christos Mdoc::def_macro('.Ed', delete $orig_ed->{run}, %$orig_ed); 110 1.1 christos $displays{ $type }[1]; 111 1.1 christos }); 112 1.1 christos $displays{ $type }[0] 113 1.1 christos }); 114 1.1 christos Mdoc::def_macro('.Ed', sub { die '.Ed used but .Bd was not seen' }); 115 1.1 christos 116 1.1 christos # Set up Bl/El 117 1.1 christos 118 1.1 christos my %lists = ( 119 1.1 christos bullet => [ '@itemize @bullet', '@end itemize' ], 120 1.1 christos tag => [ '@table @asis', '@end table' ], 121 1.1 christos column => [ '@table @asis', '@end table' ], 122 1.1 christos ); 123 1.1 christos 124 1.1 christos Mdoc::set_Bl_callback(sub { 125 1.1 christos my $type = shift; 126 1.1 christos die "Specify a list type" if not defined $type; 127 1.1 christos $type =~ s/^-//; 128 1.1 christos die "Not supported list type <$type>" if not exists $lists{ $type }; 129 1.1 christos Mdoc::set_El_callback(sub { $lists{ $type }[1] }); 130 1.1 christos $lists{ $type }[0] 131 1.1 christos }); 132 1.1 christos Mdoc::def_macro('.It', sub { '@item', hs, @_ }); 133 1.1 christos 134 1.1 christos for (qw(Aq Bq Brq Pq Qq Ql Sq Dq Eq Ar Fl Ic Pa Op Cm Li Fx Ux Va)) { 135 1.1 christos my $m = Mdoc::get_macro(".$_"); 136 1.1 christos Mdoc::def_macro($_, delete $m->{run}, %$m); 137 1.1 christos } 138 1.1 christos 139 1.1 christos sub print_line { 140 1.1 christos my $s = shift; 141 1.1 christos $s =~ s/\\&//g; 142 1.1 christos print "$s\n"; 143 1.1 christos } 144 1.1 christos 145 1.1 christos sub preprocess_args { 146 1.1 christos $_ =~ s/([{}])/\@$1/g for @_; 147 1.1 christos } 148 1.1 christos 149 1.1 christos sub run { 150 1.1 christos while (my ($macro, @args) = Mdoc::parse_line(\*STDIN, \&print_line, 151 1.1 christos \&preprocess_args) 152 1.1 christos ) { 153 1.1 christos my @ret = Mdoc::call_macro($macro, @args); 154 1.1 christos if (@ret) { 155 1.1 christos my $s = Mdoc::to_string(@ret); 156 1.1 christos print_line($s); 157 1.1 christos } 158 1.1 christos } 159 1.1 christos return 0; 160 1.1 christos } 161 1.1 christos 162 1.1 christos exit run(@ARGV) unless caller; 163