added ffplay documentation - added automatic man page generation for ffmpeg, ffserver...
authorFabrice Bellard <fabrice@bellard.org>
Sun, 24 Aug 2003 11:16:29 +0000 (11:16 +0000)
committerFabrice Bellard <fabrice@bellard.org>
Sun, 24 Aug 2003 11:16:29 +0000 (11:16 +0000)
Originally committed as revision 2136 to svn://svn.ffmpeg.org/ffmpeg/trunk

doc/Makefile
doc/ffmpeg-doc.texi
doc/ffplay-doc.texi [new file with mode: 0644]
doc/ffserver-doc.texi
doc/texi2pod.pl [new file with mode: 0755]

index ac34490..d472dc3 100644 (file)
@@ -1,7 +1,14 @@
-all: ffmpeg-doc.html faq.html ffserver-doc.html hooks.html
+all: ffmpeg-doc.html faq.html ffserver-doc.html ffplay-doc.html hooks.html \
+     ffmpeg.1 ffserver.1 ffplay.1
 
 %.html: %.texi Makefile
        texi2html -monolithic -number $<
 
+%.pod: %-doc.texi
+       ./texi2pod.pl $< $@
+
+%.1: %.pod
+       pod2man --section=1 --center=" " --release=" " $< > $@
+
 clean:
-       rm -f *.html
+       rm -f *.html *.pod *.1
index 3af1afe..7d7c7d4 100644 (file)
@@ -22,19 +22,21 @@ video on the fly with a high quality polyphase filter.
 
 @chapter Quick Start
 
+@c man begin EXAMPLES
 @section Video and Audio grabbing
 
- FFmpeg can use a video4linux compatible video source and any Open Sound
-  System audio source:
+FFmpeg can use a video4linux compatible video source and any Open Sound
+System audio source:
+
 @example
-  ffmpeg /tmp/out.mpg 
+ffmpeg /tmp/out.mpg 
 @end example
 
-  Note that you must activate the right video source and channel before
-  launching ffmpeg. You can use any TV viewer such as xawtv
-  (@url{http://bytesex.org/xawtv/}) by Gerd Knorr which I find very
-  good. You must also set correctly the audio recording levels with a
-  standard mixer.
+Note that you must activate the right video source and channel before
+launching ffmpeg. You can use any TV viewer such as xawtv
+(@url{http://bytesex.org/xawtv/}) by Gerd Knorr which I find very
+good. You must also set correctly the audio recording levels with a
+standard mixer.
 
 @section Video and Audio file format conversion
 
@@ -45,109 +47,115 @@ Examples:
 * You can input from YUV files:
 
 @example
-  ffmpeg -i /tmp/test%d.Y /tmp/out.mpg 
+ffmpeg -i /tmp/test%d.Y /tmp/out.mpg 
 @end example
 
-  It will use the files: 
+It will use the files: 
 @example
-       /tmp/test0.Y, /tmp/test0.U, /tmp/test0.V,
-       /tmp/test1.Y, /tmp/test1.U, /tmp/test1.V, etc...
+/tmp/test0.Y, /tmp/test0.U, /tmp/test0.V,
+/tmp/test1.Y, /tmp/test1.U, /tmp/test1.V, etc...
 @end example
 
-  The Y files use twice the resolution of the U and V files. They are
-  raw files, without header. They can be generated by all decent video
-  decoders. You must specify the size of the image with the '-s' option
-  if ffmpeg cannot guess it.
+The Y files use twice the resolution of the U and V files. They are
+raw files, without header. They can be generated by all decent video
+decoders. You must specify the size of the image with the @option{-s} option
+if ffmpeg cannot guess it.
 
 * You can input from a RAW YUV420P file:
 
 @example
-  ffmpeg -i /tmp/test.yuv /tmp/out.avi
+ffmpeg -i /tmp/test.yuv /tmp/out.avi
 @end example
 
-  The RAW YUV420P is a file containing RAW YUV planar, for each frame first
-  come the Y plane followed by U and V planes, which are half vertical and
-  horizontal resolution.
+The RAW YUV420P is a file containing RAW YUV planar, for each frame first
+come the Y plane followed by U and V planes, which are half vertical and
+horizontal resolution.
 
 * You can output to a RAW YUV420P file:
 
 @example
-  ffmpeg -i mydivx.avi -o hugefile.yuv
+ffmpeg -i mydivx.avi -o hugefile.yuv
 @end example
 
 * You can set several input files and output files:
 
 @example
-  ffmpeg -i /tmp/a.wav -s 640x480 -i /tmp/a.yuv /tmp/a.mpg
+ffmpeg -i /tmp/a.wav -s 640x480 -i /tmp/a.yuv /tmp/a.mpg
 @end example
 
-  Convert the audio file a.wav and the raw yuv video file a.yuv
-  to mpeg file a.mpg
+Convert the audio file a.wav and the raw yuv video file a.yuv
+to mpeg file a.mpg
 
 * You can also do audio and video conversions at the same time:
 
 @example
-  ffmpeg -i /tmp/a.wav -ar 22050 /tmp/a.mp2
+ffmpeg -i /tmp/a.wav -ar 22050 /tmp/a.mp2
 @end example
 
-  Convert the sample rate of a.wav to 22050 Hz and encode it to MPEG audio.
+Convert the sample rate of a.wav to 22050 Hz and encode it to MPEG audio.
 
 * You can encode to several formats at the same time and define a
-  mapping from input stream to output streams:
+mapping from input stream to output streams:
 
 @example
-  ffmpeg -i /tmp/a.wav -ab 64 /tmp/a.mp2 -ab 128 /tmp/b.mp2 -map 0:0 -map 0:0
+ffmpeg -i /tmp/a.wav -ab 64 /tmp/a.mp2 -ab 128 /tmp/b.mp2 -map 0:0 -map 0:0
 @end example
 
-  Convert a.wav to a.mp2 at 64 kbits and b.mp2 at 128 kbits. '-map
-  file:index' specify which input stream is used for each output
-  stream, in the order of the definition of output streams.
+Convert a.wav to a.mp2 at 64 kbits and b.mp2 at 128 kbits. '-map
+file:index' specify which input stream is used for each output
+stream, in the order of the definition of output streams.
 
 * You can transcode decrypted VOBs
 
 @example
-  ffmpeg -i snatch_1.vob -f avi -vcodec mpeg4 -b 800 -g 300 -bf 2 -acodec mp3 -ab 128 snatch.avi
+ffmpeg -i snatch_1.vob -f avi -vcodec mpeg4 -b 800 -g 300 -bf 2 -acodec mp3 -ab 128 snatch.avi
 @end example
 
-  This is a typical DVD ripper example, input from a VOB file, output
-  to an AVI file with MPEG-4 video and MP3 audio, note that in this
-  command we use B frames so the MPEG-4 stream is DivX5 compatible, GOP
-  size is 300 that means an INTRA frame every 10 seconds for 29.97 fps
-  input video.  Also the audio stream is MP3 encoded so you need LAME
-  support which is enabled using @code{--enable-mp3lame} when
-  configuring.  The mapping is particularly useful for DVD transcoding
-  to get the desired audio language.
+This is a typical DVD ripper example, input from a VOB file, output
+to an AVI file with MPEG-4 video and MP3 audio, note that in this
+command we use B frames so the MPEG-4 stream is DivX5 compatible, GOP
+size is 300 that means an INTRA frame every 10 seconds for 29.97 fps
+input video.  Also the audio stream is MP3 encoded so you need LAME
+support which is enabled using @code{--enable-mp3lame} when
+configuring.  The mapping is particularly useful for DVD transcoding
+to get the desired audio language.
 
-  NOTE: to see the supported input formats, use @code{ffmpeg -formats}.
+NOTE: to see the supported input formats, use @code{ffmpeg -formats}.
+@c man end
 
 @chapter Invocation
 
 @section Syntax
 
- The generic syntax is:
+The generic syntax is:
 
 @example 
-  ffmpeg [[options][-i input_file]]... {[options] output_file}...
+@c man begin SYNOPSIS
+ffmpeg [[options][@option{-i} @var{input_file}]]... @{[options] @var{output_file}@}...
+@c man end
 @end example
-  If no input file is given, audio/video grabbing is done.
+@c man begin DESCRIPTION
+If no input file is given, audio/video grabbing is done.
 
-  As a general rule, options are applied to the next specified
-  file. For example, if you give the '-b 64' option, it sets the video
-  bitrate of the next file. Format option may be needed for raw input
-  files.
+As a general rule, options are applied to the next specified
+file. For example, if you give the @option{-b 64} option, it sets the video
+bitrate of the next file. Format option may be needed for raw input
+files.
 
-  By default, ffmpeg tries to convert as losslessly as possible: it
-  uses the same audio and video parameter for the outputs as the one
-  specified for the inputs.
+By default, ffmpeg tries to convert as losslessly as possible: it
+uses the same audio and video parameter for the outputs as the one
+specified for the inputs.
+@c man end
 
+@c man begin OPTIONS
 @section Main options
 
-@table @samp
+@table @option
 @item -L
 show license
 @item -h
-                  show help
-@item -formats            
+show help
+@item -formats
 show available formats, codecs, protocols, ...
 @item -f fmt              
 force format
@@ -173,13 +181,11 @@ set the copyright
 @item -comment string     
 set the comment
 
-@item -b bitrate          
-set video bitrate (in kbit/s)
 @end table
 
 @section Video Options
 
-@table @samp
+@table @option
 @item -s size             
 set frame size                       [160x128]
 @item -r fps           
@@ -203,7 +209,7 @@ select two pass log file name
 
 @section Audio Options
 
-@table @samp
+@table @option
 @item -ab bitrate         
 set audio bitrate (in kbit/s)
 @item -ar freq    
@@ -218,7 +224,7 @@ set audio bitrate (in kbit/s)
 
 @section Advanced options
 
-@table @samp
+@table @option
 @item -map file:stream    
 set input stream mapping
 @item -g gop_size         
@@ -264,6 +270,22 @@ calculate PSNR of compressed frames
 @item -vstats             
 dump video coding statistics to file
 @end table
+@c man end
+
+@ignore
+
+@setfilename ffmpeg
+@settitle FFmpeg video converter
+
+@c man begin SEEALSO
+ffserver(1), ffplay(1) and the html documentation of @file{ffmpeg}.
+@c man end
+
+@c man begin AUTHOR
+Fabrice Bellard
+@c man end
+
+@end ignore
 
 @section Protocols
 
@@ -272,47 +294,47 @@ to the standard output.
 
 ffmpeg handles also many protocols specified with the URL syntax.
 
- Use 'ffmpeg -formats' to have a list of the supported protocols.
+Use 'ffmpeg -formats' to have a list of the supported protocols.
 
- The protocol @code{http:} is currently used only to communicate with
- ffserver (see the ffserver documentation). When ffmpeg will be a
- video player it will also be used for streaming :-)
+The protocol @code{http:} is currently used only to communicate with
+ffserver (see the ffserver documentation). When ffmpeg will be a
+video player it will also be used for streaming :-)
 
 @chapter Tips
 
 @itemize
 @item For streaming at very low bit rate application, use a low frame rate
-  and a small gop size. This is especially true for real video where
-  the Linux player does not seem to be very fast, so it can miss
-  frames. An example is:
+and a small gop size. This is especially true for real video where
+the Linux player does not seem to be very fast, so it can miss
+frames. An example is:
 
 @example
-  ffmpeg -g 3 -r 3 -t 10 -b 50 -s qcif -f rv10 /tmp/b.rm
+ffmpeg -g 3 -r 3 -t 10 -b 50 -s qcif -f rv10 /tmp/b.rm
 @end example
 
 @item  The parameter 'q' which is displayed while encoding is the current
-  quantizer. The value of 1 indicates that a very good quality could
-  be achieved. The value of 31 indicates the worst quality. If q=31
-  too often, it means that the encoder cannot compress enough to meet
-  your bit rate. You must either increase the bit rate, decrease the
-  frame rate or decrease the frame size.
+quantizer. The value of 1 indicates that a very good quality could
+be achieved. The value of 31 indicates the worst quality. If q=31
+too often, it means that the encoder cannot compress enough to meet
+your bit rate. You must either increase the bit rate, decrease the
+frame rate or decrease the frame size.
 
 @item If your computer is not fast enough, you can speed up the
-  compression at the expense of the compression ratio. You can use
-  '-me zero' to speed up motion estimation, and '-intra' to disable
-  completely motion estimation (you have only I frames, which means it
-  is about as good as JPEG compression).
+compression at the expense of the compression ratio. You can use
+'-me zero' to speed up motion estimation, and '-intra' to disable
+completely motion estimation (you have only I frames, which means it
+is about as good as JPEG compression).
 
 @item To have very low bitrates in audio, reduce the sampling frequency
-  (down to 22050 kHz for mpeg audio, 22050 or 11025 for ac3).
+(down to 22050 kHz for mpeg audio, 22050 or 11025 for ac3).
 
 @item To have a constant quality (but a variable bitrate), use the option
-  '-qscale n' when 'n' is between 1 (excellent quality) and 31 (worst
-  quality).
+'-qscale n' when 'n' is between 1 (excellent quality) and 31 (worst
+quality).
 
 @item When converting video files, you can use the '-sameq' option which
-  uses in the encoder the same quality factor than in the decoder. It
-  allows to be almost lossless in encoding.
+uses in the encoder the same quality factor than in the decoder. It
+allows to be almost lossless in encoding.
 
 @end itemize
 
diff --git a/doc/ffplay-doc.texi b/doc/ffplay-doc.texi
new file mode 100644 (file)
index 0000000..a1eed74
--- /dev/null
@@ -0,0 +1,104 @@
+\input texinfo @c -*- texinfo -*-
+
+@settitle FFplay Documentation
+@titlepage
+@sp 7
+@center @titlefont{FFplay Documentation}
+@sp 3
+@end titlepage
+
+
+@chapter Introduction
+
+@c man begin DESCRIPTION
+FFplay is a very simple and portable media player using the FFmpeg
+libraries and the SDL library. It is mostly used as a test bench for the
+various APIs of FFmpeg.
+@c man end
+
+@chapter Invocation
+
+@section Syntax
+@example 
+@c man begin SYNOPSIS
+ffplay [options] @file{input_file}
+@c man end
+@end example
+
+@c man begin OPTIONS
+@section Main options
+
+@table @option
+@item -h
+show help
+@item -x width
+force displayed width
+@item -y height
+force displayed height
+@item -an
+disable audio
+@item -vn
+disable video
+@item -nodisp
+disable graphical display
+@item -f fmt
+force format
+@end table
+
+@section Advanced options
+@table @option
+@item -stats
+show the stream duration, the codec parameters, the current position in
+the stream, and the audio/video synchronisation drift.
+@item -rtp_tcp
+force RTP/TCP protocol usage instead of RTP/UDP. It is only meaningful
+if you are doing stream with the RTSP protocol.
+@item -sync type
+set the master clock to audio (@code{type=audio}), video
+(@code{type=video}) or external (@code{type=ext}). Default is audio. The
+master clock is used to control audio-video synchronization. Most media
+players use audio as master clock, but in some cases (streaming or high
+quality broadcast) it is necessary to change that. This option is mainly
+used for debugging purposes.
+@end table
+
+@section While playing
+
+@table @key
+@item q, ESC
+quit
+
+@item f
+toggle full screen
+
+@item p, SPC
+pause
+
+@item a
+cycle audio channel
+
+@item v
+cycle video channel
+
+@item w
+show audio waves
+@end table
+
+@c man end
+
+@ignore
+
+@setfilename ffplay
+@settitle FFplay media player
+
+@c man begin SEEALSO
+ffmpeg(1), ffserver(1) and the html documentation of @file{ffmpeg}.
+@c man end
+
+@c man begin AUTHOR
+Fabrice Bellard
+@c man end
+
+@end ignore
+
+@bye
index ff37d3b..f6ddb71 100644 (file)
@@ -10,6 +10,7 @@
 
 @chapter Introduction
 
+@c man begin DESCRIPTION
 FFserver is a streaming server for both audio and video. It supports
 several live feeds, streaming from files and time shifting on live feeds
 (you can seek to positions in the past on each live feed, provided you
@@ -17,8 +18,9 @@ specify a big enough feed storage in ffserver.conf).
 
 This documentation covers only the streaming aspects of ffserver /
 ffmpeg. All questions about parameters for ffmpeg, codec questions,
-etc. are not covered here. Read @file{ffmpeg-doc.[texi|html]} for more
+etc. are not covered here. Read @file{ffmpeg-doc.html} for more
 information.
+@c man end
 
 @chapter QuickStart
 
@@ -182,4 +184,39 @@ in the future and so unlikely to useful.
 You use this by adding the ?date= to the end of the URL for the stream.
 For example:   @samp{http://localhost:8080/test.asf?date=2002-07-26T23:05:00}.
 
+@chapter Invocation
+@section Syntax
+@example 
+@c man begin SYNOPSIS
+ffserver [options]
+@c man end
+@end example
+
+@section Options
+@c man begin OPTIONS
+@table @option
+@item -L
+print the license
+@item -h
+print the help
+@item -f configfile
+use @file{configfile} instead of @file{/etc/ffserver.conf}
+@end table
+@c man end
+
+@ignore
+
+@setfilename ffsserver
+@settitle FFserver video server
+
+@c man begin SEEALSO
+ffmpeg(1), ffplay(1) and the html documentation of @file{ffmpeg}.
+@c man end
+
+@c man begin AUTHOR
+Fabrice Bellard
+@c man end
+
+@end ignore
+
 @bye
diff --git a/doc/texi2pod.pl b/doc/texi2pod.pl
new file mode 100755 (executable)
index 0000000..52e7050
--- /dev/null
@@ -0,0 +1,427 @@
+#! /usr/bin/perl -w
+
+#   Copyright (C) 1999, 2000, 2001 Free Software Foundation, Inc.
+
+# This file is part of GNU CC.
+
+# GNU CC is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2, or (at your option)
+# any later version.
+
+# GNU CC is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with GNU CC; see the file COPYING.  If not, write to
+# the Free Software Foundation, 59 Temple Place - Suite 330,
+# Boston MA 02111-1307, USA.
+
+# This does trivial (and I mean _trivial_) conversion of Texinfo
+# markup to Perl POD format.  It's intended to be used to extract
+# something suitable for a manpage from a Texinfo document.
+
+$output = 0;
+$skipping = 0;
+%sects = ();
+$section = "";
+@icstack = ();
+@endwstack = ();
+@skstack = ();
+@instack = ();
+$shift = "";
+%defs = ();
+$fnno = 1;
+$inf = "";
+$ibase = "";
+
+while ($_ = shift) {
+    if (/^-D(.*)$/) {
+       if ($1 ne "") {
+           $flag = $1;
+       } else {
+           $flag = shift;
+       }
+       $value = "";
+       ($flag, $value) = ($flag =~ /^([^=]+)(?:=(.+))?/);
+       die "no flag specified for -D\n"
+           unless $flag ne "";
+       die "flags may only contain letters, digits, hyphens, dashes and underscores\n"
+           unless $flag =~ /^[a-zA-Z0-9_-]+$/;
+       $defs{$flag} = $value;
+    } elsif (/^-/) {
+       usage();
+    } else {
+       $in = $_, next unless defined $in;
+       $out = $_, next unless defined $out;
+       usage();
+    }
+}
+
+if (defined $in) {
+    $inf = gensym();
+    open($inf, "<$in") or die "opening \"$in\": $!\n";
+    $ibase = $1 if $in =~ m|^(.+)/[^/]+$|;
+} else {
+    $inf = \*STDIN;
+}
+
+if (defined $out) {
+    open(STDOUT, ">$out") or die "opening \"$out\": $!\n";
+}
+
+while(defined $inf) {
+while(<$inf>) {
+    # Certain commands are discarded without further processing.
+    /^\@(?:
+        [a-z]+index            # @*index: useful only in complete manual
+        |need                  # @need: useful only in printed manual
+        |(?:end\s+)?group      # @group .. @end group: ditto
+        |page                  # @page: ditto
+        |node                  # @node: useful only in .info file
+        |(?:end\s+)?ifnottex   # @ifnottex .. @end ifnottex: use contents
+       )\b/x and next;
+
+    chomp;
+
+    # Look for filename and title markers.
+    /^\@setfilename\s+([^.]+)/ and $fn = $1, next;
+    /^\@settitle\s+([^.]+)/ and $tl = postprocess($1), next;
+
+    # Identify a man title but keep only the one we are interested in.
+    /^\@c\s+man\s+title\s+([A-Za-z0-9-]+)\s+(.+)/ and do {
+       if (exists $defs{$1}) {
+           $fn = $1;
+           $tl = postprocess($2);
+       }
+       next;
+    };
+
+    # Look for blocks surrounded by @c man begin SECTION ... @c man end.
+    # This really oughta be @ifman ... @end ifman and the like, but such
+    # would require rev'ing all other Texinfo translators.
+    /^\@c\s+man\s+begin\s+([A-Z]+)\s+([A-Za-z0-9-]+)/ and do {
+       $output = 1 if exists $defs{$2};
+        $sect = $1;
+       next;
+    };
+    /^\@c\s+man\s+begin\s+([A-Z]+)/ and $sect = $1, $output = 1, next;
+    /^\@c\s+man\s+end/ and do {
+       $sects{$sect} = "" unless exists $sects{$sect};
+       $sects{$sect} .= postprocess($section);
+       $section = "";
+       $output = 0;
+       next;
+    };
+
+    # handle variables
+    /^\@set\s+([a-zA-Z0-9_-]+)\s*(.*)$/ and do {
+       $defs{$1} = $2;
+       next;
+    };
+    /^\@clear\s+([a-zA-Z0-9_-]+)/ and do {
+       delete $defs{$1};
+       next;
+    };
+
+    next unless $output;
+
+    # Discard comments.  (Can't do it above, because then we'd never see
+    # @c man lines.)
+    /^\@c\b/ and next;
+
+    # End-block handler goes up here because it needs to operate even
+    # if we are skipping.
+    /^\@end\s+([a-z]+)/ and do {
+       # Ignore @end foo, where foo is not an operation which may
+       # cause us to skip, if we are presently skipping.
+       my $ended = $1;
+       next if $skipping && $ended !~ /^(?:ifset|ifclear|ignore|menu|iftex)$/;
+
+       die "\@end $ended without \@$ended at line $.\n" unless defined $endw;
+       die "\@$endw ended by \@end $ended at line $.\n" unless $ended eq $endw;
+
+       $endw = pop @endwstack;
+
+       if ($ended =~ /^(?:ifset|ifclear|ignore|menu|iftex)$/) {
+           $skipping = pop @skstack;
+           next;
+       } elsif ($ended =~ /^(?:example|smallexample|display)$/) {
+           $shift = "";
+           $_ = "";    # need a paragraph break
+       } elsif ($ended =~ /^(?:itemize|enumerate|[fv]?table)$/) {
+           $_ = "\n=back\n";
+           $ic = pop @icstack;
+       } else {
+           die "unknown command \@end $ended at line $.\n";
+       }
+    };
+
+    # We must handle commands which can cause skipping even while we
+    # are skipping, otherwise we will not process nested conditionals
+    # correctly.
+    /^\@ifset\s+([a-zA-Z0-9_-]+)/ and do {
+       push @endwstack, $endw;
+       push @skstack, $skipping;
+       $endw = "ifset";
+       $skipping = 1 unless exists $defs{$1};
+       next;
+    };
+
+    /^\@ifclear\s+([a-zA-Z0-9_-]+)/ and do {
+       push @endwstack, $endw;
+       push @skstack, $skipping;
+       $endw = "ifclear";
+       $skipping = 1 if exists $defs{$1};
+       next;
+    };
+
+    /^\@(ignore|menu|iftex)\b/ and do {
+       push @endwstack, $endw;
+       push @skstack, $skipping;
+       $endw = $1;
+       $skipping = 1;
+       next;
+    };
+
+    next if $skipping;
+
+    # Character entities.  First the ones that can be replaced by raw text
+    # or discarded outright:
+    s/\@copyright\{\}/(c)/g;
+    s/\@dots\{\}/.../g;
+    s/\@enddots\{\}/..../g;
+    s/\@([.!? ])/$1/g;
+    s/\@[:-]//g;
+    s/\@bullet(?:\{\})?/*/g;
+    s/\@TeX\{\}/TeX/g;
+    s/\@pounds\{\}/\#/g;
+    s/\@minus(?:\{\})?/-/g;
+    s/\\,/,/g;
+
+    # Now the ones that have to be replaced by special escapes
+    # (which will be turned back into text by unmunge())
+    s/&/&amp;/g;
+    s/\@\{/&lbrace;/g;
+    s/\@\}/&rbrace;/g;
+    s/\@\@/&at;/g;
+
+    # Inside a verbatim block, handle @var specially.
+    if ($shift ne "") {
+       s/\@var\{([^\}]*)\}/<$1>/g;
+    }
+
+    # POD doesn't interpret E<> inside a verbatim block.
+    if ($shift eq "") {
+       s/</&lt;/g;
+       s/>/&gt;/g;
+    } else {
+       s/</&LT;/g;
+       s/>/&GT;/g;
+    }
+
+    # Single line command handlers.
+
+    /^\@include\s+(.+)$/ and do {
+       push @instack, $inf;
+       $inf = gensym();
+
+       # Try cwd and $ibase.
+       open($inf, "<" . $1) 
+           or open($inf, "<" . $ibase . "/" . $1)
+               or die "cannot open $1 or $ibase/$1: $!\n";
+       next;
+    };
+
+    /^\@(?:section|unnumbered|unnumberedsec|center)\s+(.+)$/
+       and $_ = "\n=head2 $1\n";
+    /^\@subsection\s+(.+)$/
+       and $_ = "\n=head3 $1\n";
+
+    # Block command handlers:
+    /^\@itemize\s+(\@[a-z]+|\*|-)/ and do {
+       push @endwstack, $endw;
+       push @icstack, $ic;
+       $ic = $1;
+       $_ = "\n=over 4\n";
+       $endw = "itemize";
+    };
+
+    /^\@enumerate(?:\s+([a-zA-Z0-9]+))?/ and do {
+       push @endwstack, $endw;
+       push @icstack, $ic;
+       if (defined $1) {
+           $ic = $1 . ".";
+       } else {
+           $ic = "1.";
+       }
+       $_ = "\n=over 4\n";
+       $endw = "enumerate";
+    };
+
+    /^\@([fv]?table)\s+(\@[a-z]+)/ and do {
+       push @endwstack, $endw;
+       push @icstack, $ic;
+       $endw = $1;
+       $ic = $2;
+       $ic =~ s/\@(?:samp|strong|key|gcctabopt|option|env)/B/;
+       $ic =~ s/\@(?:code|kbd)/C/;
+       $ic =~ s/\@(?:dfn|var|emph|cite|i)/I/;
+       $ic =~ s/\@(?:file)/F/;
+       $_ = "\n=over 4\n";
+    };
+
+    /^\@((?:small)?example|display)/ and do {
+       push @endwstack, $endw;
+       $endw = $1;
+       $shift = "\t";
+       $_ = "";        # need a paragraph break
+    };
+
+    /^\@itemx?\s*(.+)?$/ and do {
+       if (defined $1) {
+           # Entity escapes prevent munging by the <> processing below.
+           $_ = "\n=item $ic\&LT;$1\&GT;\n";
+       } else {
+           $_ = "\n=item $ic\n";
+           $ic =~ y/A-Ya-y/B-Zb-z/;
+           $ic =~ s/(\d+)/$1 + 1/eg;
+       }
+    };
+
+    $section .= $shift.$_."\n";
+}
+# End of current file.
+close($inf);
+$inf = pop @instack;
+}
+
+die "No filename or title\n" unless defined $fn && defined $tl;
+
+$sects{NAME} = "$fn \- $tl\n";
+$sects{FOOTNOTES} .= "=back\n" if exists $sects{FOOTNOTES};
+
+for $sect (qw(NAME SYNOPSIS DESCRIPTION OPTIONS EXAMPLES ENVIRONMENT FILES
+             BUGS NOTES FOOTNOTES SEEALSO AUTHOR COPYRIGHT)) {
+    if(exists $sects{$sect}) {
+       $head = $sect;
+       $head =~ s/SEEALSO/SEE ALSO/;
+       print "=head1 $head\n\n";
+       print scalar unmunge ($sects{$sect});
+       print "\n";
+    }
+}
+
+sub usage
+{
+    die "usage: $0 [-D toggle...] [infile [outfile]]\n";
+}
+
+sub postprocess
+{
+    local $_ = $_[0];
+
+    # @value{foo} is replaced by whatever 'foo' is defined as.
+    while (m/(\@value\{([a-zA-Z0-9_-]+)\})/g) {
+       if (! exists $defs{$2}) {
+           print STDERR "Option $2 not defined\n";
+           s/\Q$1\E//;
+       } else {
+           $value = $defs{$2};
+           s/\Q$1\E/$value/;
+       }
+    }
+
+    # Formatting commands.
+    # Temporary escape for @r.
+    s/\@r\{([^\}]*)\}/R<$1>/g;
+    s/\@(?:dfn|var|emph|cite|i)\{([^\}]*)\}/I<$1>/g;
+    s/\@(?:code|kbd)\{([^\}]*)\}/C<$1>/g;
+    s/\@(?:gccoptlist|samp|strong|key|option|env|command|b)\{([^\}]*)\}/B<$1>/g;
+    s/\@sc\{([^\}]*)\}/\U$1/g;
+    s/\@file\{([^\}]*)\}/F<$1>/g;
+    s/\@w\{([^\}]*)\}/S<$1>/g;
+    s/\@(?:dmn|math)\{([^\}]*)\}/$1/g;
+
+    # Cross references are thrown away, as are @noindent and @refill.
+    # (@noindent is impossible in .pod, and @refill is unnecessary.)
+    # @* is also impossible in .pod; we discard it and any newline that
+    # follows it.  Similarly, our macro @gol must be discarded.
+
+    s/\(?\@xref\{(?:[^\}]*)\}(?:[^.<]|(?:<[^<>]*>))*\.\)?//g;
+    s/\s+\(\@pxref\{(?:[^\}]*)\}\)//g;
+    s/;\s+\@pxref\{(?:[^\}]*)\}//g;
+    s/\@noindent\s*//g;
+    s/\@refill//g;
+    s/\@gol//g;
+    s/\@\*\s*\n?//g;
+
+    # @uref can take one, two, or three arguments, with different
+    # semantics each time.  @url and @email are just like @uref with
+    # one argument, for our purposes.
+    s/\@(?:uref|url|email)\{([^\},]*)\}/&lt;B<$1>&gt;/g;
+    s/\@uref\{([^\},]*),([^\},]*)\}/$2 (C<$1>)/g;
+    s/\@uref\{([^\},]*),([^\},]*),([^\},]*)\}/$3/g;
+
+    # Turn B<blah I<blah> blah> into B<blah> I<blah> B<blah> to
+    # match Texinfo semantics of @emph inside @samp.  Also handle @r
+    # inside bold.
+    s/&LT;/</g;
+    s/&GT;/>/g;
+    1 while s/B<((?:[^<>]|I<[^<>]*>)*)R<([^>]*)>/B<$1>${2}B</g;
+    1 while (s/B<([^<>]*)I<([^>]+)>/B<$1>I<$2>B</g);
+    1 while (s/I<([^<>]*)B<([^>]+)>/I<$1>B<$2>I</g);
+    s/[BI]<>//g;
+    s/([BI])<(\s+)([^>]+)>/$2$1<$3>/g;
+    s/([BI])<([^>]+?)(\s+)>/$1<$2>$3/g;
+
+    # Extract footnotes.  This has to be done after all other
+    # processing because otherwise the regexp will choke on formatting
+    # inside @footnote.
+    while (/\@footnote/g) {
+       s/\@footnote\{([^\}]+)\}/[$fnno]/;
+       add_footnote($1, $fnno);
+       $fnno++;
+    }
+
+    return $_;
+}
+
+sub unmunge
+{
+    # Replace escaped symbols with their equivalents.
+    local $_ = $_[0];
+
+    s/&lt;/E<lt>/g;
+    s/&gt;/E<gt>/g;
+    s/&lbrace;/\{/g;
+    s/&rbrace;/\}/g;
+    s/&at;/\@/g;
+    s/&amp;/&/g;
+    return $_;
+}
+
+sub add_footnote
+{
+    unless (exists $sects{FOOTNOTES}) {
+       $sects{FOOTNOTES} = "\n=over 4\n\n";
+    }
+
+    $sects{FOOTNOTES} .= "=item $fnno.\n\n"; $fnno++;
+    $sects{FOOTNOTES} .= $_[0];
+    $sects{FOOTNOTES} .= "\n\n";
+}
+
+# stolen from Symbol.pm
+{
+    my $genseq = 0;
+    sub gensym
+    {
+       my $name = "GEN" . $genseq++;
+       my $ref = \*{$name};
+       delete $::{$name};
+       return $ref;
+    }
+}