1#!/usr/bin/perl 2# $Id: $ 3# Written by Adrian Mariano, additional features by Eric Backus and 4# Jeff Conrad. 5 6# Script to translate a texinfo file into an nroff/troff manual page. 7# last revision: 20 January 2014 Jeff Conrad 8 9$version="1.01s"; 10 11$html=0; 12$example=0; 13$ignore=0; 14$tex=0; 15$doman=0; 16$title=0; 17$diditem=0; 18$justdidlp=1; 19$noman=0; 20$manprefix=""; 21$args=($#ARGV < 0) ? "stdin" : "@ARGV"; 22 23printf(".\\\"Do not edit this file. It was created from %s\n", $args); 24printf(".\\\"using texi2man version %s on %s", $version, `date`); 25 26while(<>) 27{ 28 # use font CW in tables 29 if (/\@c man\s+l\s/) 30 { 31 s/\@c man //; 32 s/l/lfCWp-1/; 33 print; 34 next; 35 } 36 if (s/\@c man //) 37 { 38 print; 39 if (/\.TH/) { add_extensions(); } 40 next; 41 } 42 if (/\@c noman/) { $noman=1; next; } 43 if (/\@c end noman/) { $noman=0; next; } 44 if ($noman) { next; } 45 46 if (/\@c ifman\s*(.*)/) { $doman=1; $manprefix = $1; next; } 47 if (/\@c end ifman/) { $doman=0; $manprefix = ""; next; } 48 49 if (/^\\input/) { next; } 50 if (/^\*/) { next; } 51 if (/^START-INFO-DIR-ENTRY/) { next; } 52 if (/^END-INFO-DIR-ENTRY/) { next; } 53 54 if (/\@titlepage/) { $title=1; next; } 55 if (/\@end titlepage/) { $title=0; next; } 56 if (/\@tex/) { $tex=1; next; } 57 if (/\@end tex/) { $tex=0; next; } 58 if (/\@ignore/) { $ignore=1; next; } 59 if (/\@end ignore/) { $ignore=0; next; } 60 if (/\@ifhtml/) { $html=1; next; } 61 if (/\@end ifhtml/) { $html=0; next; } 62 if (!$doman && ($ignore || $html || $title || $tex)) { next; } 63 if (/\@codequoteundirected/) { next; } 64 65 s/\@\*$/\n\.br/g; 66 s/^\@\*/.br/g; 67 s/\@\*$/\n.br/g; 68 s/\@ / /g; 69 s/\@dmn\{([^}]*)}/\\|$1/g; 70 s/\@tie\{}/\@no_break_space\{}/g; 71 s/\@w\{}/\@no_break_space\{}/g; 72 s/\@backslashchar\{}/\\e/g; 73 74 # ellipsis, defined in extensions 75 s/\@dots\{}/\\*(El/g; 76 77 s/\@cite\{([^}]*)}/\@in_sgl_quotes\{$1}/g; 78 s/\@url\{([^}]*)}/\@in_sgl_quotes\{$1}/g; 79 s/\@email\{([^}]*)}/\@in_sgl_quotes\{$1}/g; 80 81 s/\@dfn\{([^}]*)}/\@in_italics\{$1}/g; 82 83 s/\@emph\{([^}]*)}/\@in_italics\{$1}/g; 84 s/\@i\{([^}]*)}/\@in_italics\{$1}/g; 85 s/\@r\{([^}]*)}/\@in_roman\{$1}/g; 86 s/\@var\{([^}]*)}/\@in_italics\{$1}/g; 87 88 s/\@b\{([^}]*)}/\@in_bold\{$1}/g; 89 s/\@strong\{([^}]*)}/\@in_bold\{$1}/g; 90 91 # remove trailing comma from xref because man won't include the page number 92 s/\@xref\{([^}]*)},/\@xref\{$1}/g; 93 s/\@xref\{([^}]*)}/See \@in_italics\{$1}/g; 94 s/\@ref\{([^}]*)}/\@ref\{$1}/g; 95 s/\@ref\{([^}]*)}/\@in_italics\{$1}/g; 96 s/\@pxref\{([^}]*)}/see \@in_italics\{$1}/g; 97 s/\@uref\{([^}]*)}/\@in_roman\{$1}/g; 98 99 if (/\@chapter.*\@command/) 100 { 101 s/\@command\{([^}]*)}/\@in_italics\{$1}/g; 102 } 103 104 # show in constant-width font 105 s/\@code\{([^}]*)}/\@constwid\{$1}/g; 106 s/\@command\{([^}]*)}/\@constwid\{$1}/g; 107 s/\@env\{([^}]*)}/\@constwid\{$1}/g; 108 109 # show in constant-width oblique font 110 s/\@kbd\{([^}]*)}/\@constwidI\{$1}/g; 111 112 # show in constant-width font with single quotes 113 s/\@file\{([^}]*)}/\@constwidQ\{$1}/g; 114 s/\@option\{([^}]*)}/\@constwidQ\{$1}/g; 115 # Pass ASCII double quotes to .CQ encoded as two double quotes 116 # disallow single quotes here because groff converts them to 117 # typographical closing quotes. 118 # This substitution works only in very limited circumstances, and 119 # needs extension to handle the general case of ASCII quotes in 120 # sample text 121 s/\@samp\{([^}]*)["']{2,2}}/\@samp\{$1""""}/g; 122 s/\@samp\{(.*\@(tie|no_break_space)\{})["']{2,2}}/\@samp\{$1""""}/g; 123 s/\@samp\{([^}]*)}/\@constwidQ\{$1}/g; 124 125 s/\@sc\{([^}]*)}/\@to_upper\{$1}/g; 126 127 s/\@key\{([^}]*)}/\@in_italics\{$1}/g; 128 s/\@footnote\{([^}]*)}/\@in_square_br\{$1}/g; 129 130 s/\@math\{([^}]*)}/\@no_decoration\{$1}/g; 131 132 if (/\@w\{([^}]*)}/) { 133 s/\@w\{([^}]*)}/\@no_break_word\{$1}/g; 134 } 135 136 s/\@minus\{}/\\-/g; 137 s/\@copyright\{}/\\(co/g; 138 s/\@noindent//; 139 s/\@\{/{/g; 140 s/\@}/}/g; 141 s/\@\@/@/g; 142 s/---/\\(em/g; 143 144 s/\@in_sgl_quotes\{([^}]+)}/`$1'/g; 145 s/\@in_dbl_quotes\{([^}]+)}/\"$1\"/g; 146 s/\@in_italics\{([^}]+)}/\\fI$1\\fP/g; 147 s/\@in_roman\{([^}]+)}/\\fR$1\\fP/g; 148 s/\@in_bold\{([^}]+)}/\\fB$1\\fP/g; 149 s/\@to_upper\{([^}]*)}/\U$1\E/g; 150 s/\@no_decoration\{([^}]*)}/$1/g; 151 if (/\@no_break_word\{([^}]+)}(\S*)/) { 152 $_ = no_break_word("$_", '@no_break_word'); 153 } 154 s/\@no_break_space\{}/\\ /g; 155 s/\@[ ]/ /g; 156 s/\@in_angle_br\{([^}]*)}/<$1>/g; 157 s/\@in_square_br\{([^}]*)}/[$1]/g; 158 159 # set up to use CW, CI, and CQ macros 160 # put every instance on a new line 161 # ensure that prepended and appended macros go on separate lines 162 # separate concatenated commands with spaces 163 s/([}])(\@constwid[IQ]*)/$1 $2/g; 164 s/(\@constwid[IQ]*\{[^}]+})(\@)/$1 $2/g; 165 # space before -> newline 166 s/\s+(\S*\@constwid[IQ]*\{[^}]+}\S*)/\n$1/g; 167 # space after -> newline 168 s/(\S*\@constwid[IQ]*\{[^}]+}\S*)\s+/$1\n/g; 169 170 if (/(\S*)\@constwidI\{([^}]+)}(\S*)/) { 171 $_ = CW_macro("$_", '@constwidI', ".CI"); 172 } 173 if (/(\S*)\@constwidQ\{([^}]+)}(\S*)/) { 174 $_ = CW_macro("$_", '@constwidQ', ".CQ"); 175 } 176 if (/(\S*)\@constwid\{([^}]+)}(\S*)/) { 177 $_ = CW_macro("$_", '@constwid', ".CW"); 178 } 179 180 # handle backslash character in sample 181 s/(\.C[IQW]\s+\S+\s+)"\\"/$1"\\e"/g; 182 s/(\.C[IQW]\s+)"\\"/$1"\\e"/g; 183 # handle backslash character in Windows pathname 184 # starts with a drive specifier ... 185 if (/(\.C[IQW]\s+"[[:alpha:]]:)/) { 186 # don't change font switches or escaped spaces 187 s/(\S)\\(?!(\s|f[RIBP]|f\([A-Z]{2}))/$1\\e/g; 188 } 189 # some versions of n/troff don't have \(en, so use \- 190 # don't replace double hyphens in C[IQW] macros; assume true en 191 # dashes will be closed up to previous word 192 s/([^" ]+)--/$1\\-/g; 193 194 s/\@value\{([^\s]+)}/$value{$1}/eg; 195 if (/\@set\s+([^\s]+)\s+(.*)$/) { $value{$1} = $2; next; } 196 if (/\@clear\s+([^\s]+)\s+(.*)$/) { delete $value{$1}; next; } 197 198 # works only for @item and @itemx as used in units(1) 199 if (/\@itemx (.*)/) 200 { 201 $samp = $1; 202 # add hair space to visually separate the hyphens in roman type 203 $samp =~ s/--/-\\^-/; 204 $samp =~ s/-([[:alnum:]])/-\\^$1/; 205 if (!$diditem) 206 { printf(".TP\n.BR \"$samp\""); } 207 else 208 { printf(" \", \" \"$samp\""); } 209 $diditem=1; next; 210 } 211 elsif ($diditem) { printf("\n"); $diditem=0; } 212 if (/\@item (.*)/) 213 { 214 $samp = $1; 215 # add hair space to visually separate the hyphens in roman type 216 $samp =~ s/--/-\\^-/; 217 $samp =~ s/-([[:alnum:]])/-\\^$1/; 218 printf("%s.TP\n%s.BR \"$samp\"", $manprefix, $manprefix); 219 $diditem=1; 220 next; 221 } 222 223 if (s/\@chapter (.*)/.SH \U$1\E/) 224 { 225 # restore proper case on font switches 226 s/\\FR/\\fR/g; 227 s/\\FI/\\f(BI/g; # chapter headings (SH in man) are bold 228 s/\\FP/\\fP/g; 229 printf("%s%s", $manprefix, $_); 230 $justdidlp=1; 231 next; 232 } 233 234 if (s/\@section (.*)/$1/) 235 { 236 printf("%s.SS %s", $manprefix, $_); 237 next; 238 } 239 240 # FIXME? why do we need $manprefix for these? 241 # input/output example macros 242 if (/\@example/) { printf("%s.ES\n", $manprefix); $example=1; next; } 243 if (/\@end example/) { printf("%s.EE\n", $manprefix); $example=0; $justdidlp=0; next; } 244 245 if (/\@smallexample/) { printf("%s.ES S\n", $manprefix); $example=1; next; } 246 if (/\@end smallexample/) { printf("%s.EE\n", $manprefix); $example=0; $justdidlp=0; next; } 247 248 # no CW font 249 if (/\@display/) { printf("%s.DS\n", $manprefix, $manprefix); $example=1; next; } 250 if (/\@end display/) { printf("%s.DE\n", $manprefix, $manprefix); $example=0; next; } 251 252 # no CW font, no indent 253 if (/\@format/) { printf("%s.nf\n", $manprefix); $example=1; next; } 254 if (/\@end format/) { printf("%s.fi\n", $manprefix); $example=0; next; } 255 256 257 if ($example) { s/\\\s*$/\\e\n/ }; 258 259 if (!$example && /^\s*$/ && !$doman) 260 { 261 if ($justdidlp) { next; } 262 printf(".PP\n"); 263 $justdidlp=1; 264 next; 265 } 266 267 if (/^\@/) { next; } 268 269 printf("%s%s", $manprefix, $_); 270 271 if (!$doman) { $justdidlp=0; } 272} 273 274# Extensions to legacy man macro package. groff loads the man macro file 275# after the call of TH, so these definitions must likewise follow that 276# call of TH if they are overwrite any groff extensions with the same 277# names that might be added in the future. 278 279sub add_extensions 280{ 281 # ensure that ASCII circumflex U+005E (^) is not remapped with groff 282 printf(".\\\"\n"); 283 printf(".\\\" ensure that ASCII circumflex U+005E (^) is not remapped with groff\n"); 284 printf(".if \\n(.g .tr ^\\(ha\n"); 285 286 # ellipsis: space periods with troff but not with nroff 287 printf(".\\\" ellipsis: space periods with troff but not with nroff\n"); 288 printf(".if n .ds El \\&...\n"); 289 printf(".if t .ds El \\&.\\ .\\ .\n"); 290 291 # constant-width font 292 printf(".\\\"\n"); 293 printf(".\\\" Extensions to man macros\n"); 294 printf(".\\\"\n"); 295 printf(".\\\" Constant-width font\n"); 296 printf(".de CW\n"); 297 printf(".hy 0\n"); 298 # just single quotes with nroff 299 printf(".if n \\{\\\n"); 300 printf(".ie \\\\n(.\$>2 \\&\\\\\$1'\\\\\$2'\\\\\$3\n"); 301 printf(".el \\&'\\\\\$1'\\\\\$2\n"); 302 printf(".\\}\n"); 303 # constant-width font with troff 304 printf(".if t \\{\\\n"); 305 printf(".ie \\\\n(.\$>2 \\&\\\\\$1\\f(CW\\\\\$2\\fR\\\\\$3\n"); 306 printf(".el \\&\\f(CW\\\\\$1\\fR\\\\\$2\n"); 307 printf(".\\}\n"); 308 printf(".hy 14\n"); 309 printf("..\n"); 310 311 # constant-width oblique font 312 printf(".\\\" Constant-width oblique font\n"); 313 printf(".de CI\n"); 314 printf(".hy 0\n"); 315 # single quotes with nroff 316 printf(".if n \\{\\\n"); 317 printf(".ie \\\\n(.\$>2 \\&\\\\\$1'\\fI\\\\\$2\\fR'\\\\\$3\n"); 318 printf(".el \\&'\\fI\\\\\$1\\fR'\\\\\$2\n"); 319 printf(".\\}\n"); 320 # constant-width oblique font with troff 321 printf(".if t \\{\\\n"); 322 printf(".ie \\\\n(.\$>2 \\&\\\\\$1\\f(CI\\\\\$2\\fR\\\\\$3\n"); 323 printf(".el \\&\\f(CI\\\\\$1\\fR\\\\\$2\n"); 324 printf(".\\}\n"); 325 printf(".hy 14\n"); 326 printf("..\n"); 327 328 # constant-width font with quotes with troff 329 printf(".\\\" Constant-width font with quotes\n"); 330 printf(".de CQ\n"); 331 printf(".hy 0\n"); 332 # just single quotes with nroff 333 printf(".if n \\{\\\n"); 334 printf(".ie \\\\n(.\$>2 \\&\\\\\$1'\\\\\$2'\\\\\$3\n"); 335 printf(".el \\&'\\\\\$1'\\\\\$2\n"); 336 printf(".\\}\n"); 337 # constant-width font with troff 338 printf(".if t \\{\\\n"); 339 # quotes passed as literal text encoded as \(fm 340 # make it a double quote because groff converts ` and ' to opening and 341 # closing quotes 342 printf(".ie \\\\n(.\$>2 \\&\\\\\$1`\\f(CW\\\\\$2\\fR'\\\\\$3\n"); 343 printf(".el \\&`\\f(CW\\\\\$1\\fR'\\\\\$2\n"); 344 printf(".\\}\n"); 345 printf(".hy 14\n"); 346 printf("..\n"); 347 348 # Display Start--indent, no fill 349 printf(".\\\" Display start\n"); 350 printf(".de DS\n"); 351 printf(".hy 0\n"); 352 printf(".if t .in +4n\n"); 353 printf(".if n .in +3n\n"); 354 printf(".nf\n"); 355 printf("..\n"); 356 357 # Display End 358 printf(".\\\" Display end\n"); 359 printf(".de DE\n"); 360 printf(".fi\n"); 361 printf(".in\n"); 362 printf(".hy 14\n"); 363 printf("..\n"); 364 365 # Example Start--like display, but with font CW 366 printf(".\\\" Example start\n"); 367 printf(".de ES\n"); 368 # call before size or font change to get consistent indent 369 printf(".DS\n"); 370 # CW font with troff; optionally reduce size 371 printf(".if t \\{\\\n"); 372 printf(".if '\\\\\$1'S' \\{\\\n"); 373 printf(".nr Ex 1\n"); 374 printf(".ps -1\n"); 375 printf(".\\}\n"); 376 printf(".el .nr Ex 0\n"); 377 printf(".nr mE \\\\n(.f\n"); 378 printf(".ft CW\n"); 379 printf(".\\}\n"); 380 printf("..\n"); 381 382 # Example End 383 printf(".\\\" Example end\n"); 384 printf(".de EE\n"); 385 # restore font and size with troff 386 printf(".if t \\{\\\n"); 387 printf(".ft \\\\n(mE\n"); 388 printf(".if \\\\n(Ex=1 .ps\n"); 389 printf(".\\}\n"); 390 printf(".DE\n"); 391 printf("..\n"); 392} 393 394# convert texinfo commands to .C[IQW] macros 395sub CW_macro 396{ 397 my $line = shift; 398 my $from = shift; 399 my $to = shift; 400 401 # prepended and appended punctuation 402 $line =~ s/(\S+)$from\{([^}]+)}(\S+)/$to $1 "$2" $3/g; 403 # prepended punctuation 404 $line =~ s/(\S+)$from\{([^}]+)}/$to $1 "$2" ""/g; 405 # appended punctuation 406 $line =~ s/$from\{([^}]+)}(\S+)/$to "$1" $2/g; 407 # just the argument 408 $line =~ s/$from\{([^}]+)}/$to "$1"/g; 409 410 return $line; 411} 412 413# convert all spaces within @w{...} to unbreakable 414sub no_break_word 415{ 416 my $line = shift; 417 my $pattern = (shift) . "\{"; 418 my $len = length($pattern); 419 my $ndx = -1; 420 my $bracelevel = 0; 421 my $char; 422 423 while (($ndx = index($line, $pattern, $ndx)) > -1) { 424 # get rid of the @ command and opening brace 425 substr($line, $ndx, $len, ''); 426 $bracelevel = 1; 427 while ($bracelevel > 0) { 428 $char = substr($line, $ndx, 1); 429 # end of line and braces not closed 430 if ($char eq "") { 431 last; 432 } 433 elsif ($char eq '{') { 434 $bracelevel++; 435 } 436 elsif ($char eq '}') { 437 $bracelevel--; 438 } 439 # make spaces nonbreaking 440 if ($char eq ' ') { 441 substr($line, $ndx++, 1, '\ '); 442 $ndx++; 443 # assume multiple spaces are not wanted 444 while (substr($line, $ndx, 1) eq ' ') { 445 substr($line, $ndx, 1, ''); 446 } 447 } 448 $ndx++; 449 } 450 # get rid of the closing brace for the @ command. This should 451 # always be true unless there�s an internal brace mismatch ... 452 if (substr($line, $ndx - 1, 1) eq '}' ) { 453 substr($line, $ndx - 1, 1, ''); 454 } 455 else { 456 die "Missing closing '}'"; 457 } 458 } 459 460 return $line; 461} 462 463 464