fakeinetd: attempted ipv6-ization (and it's done)
[oweals/busybox.git] / docs / autodocifier.pl
index 2ce1edd7530fee02aabb064cd7df6dca87ed6348..3aa838eb40cc74b5ba4f8cb7cf1bd778e30b75dd 100755 (executable)
 #!/usr/bin/perl -w
-#
-# autodocufier.pl - extracts usage messages from busybox usage.c and
-# pretty-prints them to stdout.
 
 use strict;
+use Getopt::Long;
+
+# collect lines continued with a '\' into an array
+sub continuation {
+       my $fh = shift;
+       my @line;
+
+       while (<$fh>) {
+               my $s = $_;
+               $s =~ s/\\\s*$//;
+               #$s =~ s/#.*$//;
+               push @line, $s;
+               last unless (/\\\s*$/);
+       }
+       return @line;
+}
 
-my $line;
-my $applet;
-my $count;
-my $full_usage;
-
-open(USAGE, 'usage.h') or die "usage.h: $!";
-
-while (defined($line = <USAGE>)) {
-       $count=0;
-       if ($line =~ /^#define (\w+)_trivial_usage/) {
-               # grab the applet name
-               $applet = $1;
-               print "\n$applet:\n";
-
-               while (defined($line = <USAGE>)) {
-                       if ( $count==0 ) {
-                           $count++;
-                           print "\t$applet ";
-                       } else { print "\t"; }
-                       $full_usage = $applet . "_full_usage";
-                       last if ( $line =~ /$full_usage/ );
-                       # Skip preprocessor stuff
-                       next if $line =~ /^\s*#/;
-                       # Strip the continuation char
-                       $line =~ s/\\$//;
-                       # strip quotes off
-                       $line =~ s/^\s*"//;
-                       $line =~ s/"\s*$//;
-                       # substitute escape sequences
-                       # (there's probably a better way to do this...)
-                       $line =~ s/\\t/ /g;
-                       $line =~ s/\\n//g;
-                       # fix up preprocessor macros
-                       $line =~ s/USAGE_\w+\([\s]*?(".*?").*?\)/$1/sg;
-                       # Strip any empty quotes out
-                       $line =~ s/"[\s]*"//sg;
-                       # strip line end quotes, again
-                       $line =~ s/^\s*"//;
-                       $line =~ s/"\s*$//;
-
-                       # Finally, print it
-                       print "$line\n";
+# regex && eval away unwanted strings from documentation
+sub beautify {
+       my $text = shift;
+       for (;;) {
+               my $text2 = $text;
+               $text =~ s/SKIP_\w+\(.*?"\s*\)//sxg;
+               $text =~ s/USE_\w+\(\s*?(.*?)"\s*\)/$1"/sxg;
+               $text =~ s/USAGE_\w+\(\s*?(.*?)"\s*\)/$1"/sxg;
+               last if ( $text2 eq $text );
+       }
+       $text =~ s/"\s*"//sg;
+       my @line = split("\n", $text);
+       $text = join('',
+               map {
+                       s/^\s*"//;
+                       s/"\s*$//;
+                       s/%/%%/g;
+                       s/\$/\\\$/g;
+                       eval qq[ sprintf(qq{$_}) ]
+               } @line
+       );
+       return $text;
+}
+
+# generate POD for an applet
+sub pod_for_usage {
+       my $name  = shift;
+       my $usage = shift;
+
+       # Sigh.  Fixup the known odd-name applets.
+       $name =~ s/dpkg_deb/dpkg-deb/g;
+       $name =~ s/fsck_minix/fsck.minix/g;
+       $name =~ s/mkfs_minix/mkfs.minix/g;
+       $name =~ s/run_parts/run-parts/g;
+       $name =~ s/start_stop_daemon/start-stop-daemon/g;
+
+       # make options bold
+       my $trivial = $usage->{trivial};
+       if (!defined $usage->{trivial}) {
+               $trivial = "";
+       } else {
+               $trivial =~ s/(?<!\w)(-\w+)/B<$1>/sxg;
+       }
+       my @f0 =
+               map { $_ !~ /^\s/ && s/(?<!\w)(-\w+)/B<$1>/g; $_ }
+               split("\n", (defined $usage->{full} ? $usage->{full} : ""));
+
+       # add "\n" prior to certain lines to make indented
+       # lines look right
+       my @f1;
+       my $len = @f0;
+       for (my $i = 0; $i < $len; $i++) {
+               push @f1, $f0[$i];
+               if (($i+1) != $len && $f0[$i] !~ /^\s/ && $f0[$i+1] =~ /^\s/) {
+                       next if ($f0[$i] =~ /^$/);
+                       push(@f1, "") unless ($f0[$i+1] =~ /^\s*$/s);
                }
-               printf("\n");
-               while (defined($line = <USAGE>)) {
-                       if ( $count==0 ) {
-                           $count++;
-                           print "\t$applet ";
-                       } else { print "\t"; }
-                       # we're done if we hit a line lacking a '\' at the end
-                       #last if ! $line !~ /\\$/;
-                       if ( $line !~ /\\$/ ) {
-                           #print "Got one at $line\n";
-                           last;
-                       }
-                       # Skip preprocessor stuff
-                       next if $line =~ /^\s*#/;
-                       # Strip the continuation char
-                       $line =~ s/\\$//;
-                       # strip quotes off
-                       $line =~ s/^\s*"//;
-                       $line =~ s/"\s*$//;
-                       # substitute escape sequences
-                       # (there's probably a better way to do this...)
-                       $line =~ s/\\t/ /g;
-                       $line =~ s/\\n//g;
-                       # Automagically #define all preprocessor lines
-                       #$line =~ s/USAGE_\w+\([\s]*?(".*?")\s,\s".*"\s\)/$1/sg;
-                       $line =~ s/USAGE_\w+\(\s*?(".*").*\)/$1/sg;
-                       # Strip any empty quotes out
-                       $line =~ s/"[\s]*"//sg;
-                       # strip line end quotes, again
-                       $line =~ s/^\s*"//;
-                       $line =~ s/"\s*$//;
-
-                       # Finally, print it
-                       print "$line\n";
+       }
+       my $full = join("\n", @f1);
+
+       # prepare notes if they exist
+       my $notes = (defined $usage->{notes})
+               ? "$usage->{notes}\n\n"
+               : "";
+
+       # prepare examples if they exist
+       my $example = (defined $usage->{example})
+               ?
+                       "Example:\n\n" .
+                       join ("\n",
+                       map  { "\t$_" }
+                       split("\n", $usage->{example})) . "\n\n"
+               : "";
+
+       # Pad the name so that the applet name gets a line
+       # by itself in BusyBox.txt
+       my $spaces = 10 - length($name);
+       if ($spaces > 0) {
+               $name .= " " x $spaces;
+       }
+
+       return
+               "=item B<$name>".
+               "\n\n$name $trivial\n\n".
+               "$full\n\n"   .
+               "$notes"  .
+               "$example" .
+               "\n\n"
+       ;
+}
+
+# the keys are applet names, and
+# the values will contain hashrefs of the form:
+#
+# {
+#     trivial => "...",
+#     full    => "...",
+#     notes   => "...",
+#     example => "...",
+# }
+my %docs;
+
+
+# get command-line options
+
+my %opt;
+
+GetOptions(
+       \%opt,
+       "help|h",
+       "pod|p",
+       "verbose|v",
+);
+
+if (defined $opt{help}) {
+       print
+               "$0 [OPTION]... [FILE]...\n",
+               "\t--help\n",
+               "\t--pod\n",
+               "\t--verbose\n",
+       ;
+       exit 1;
+}
+
+
+# collect documenation into %docs
+
+foreach (@ARGV) {
+       open(USAGE, $_) || die("$0: $_: $!");
+       my $fh = *USAGE;
+       my ($applet, $type, @line);
+       while (<$fh>) {
+               if (/^#define (\w+)_(\w+)_usage/) {
+                       $applet = $1;
+                       $type   = $2;
+                       @line   = continuation($fh);
+                       my $doc = $docs{$applet} ||= { };
+                       my $text      = join("\n", @line);
+                       $doc->{$type} = beautify($text);
                }
-               printf("\n\n");
        }
 }
+
+
+# generate structured documentation
+
+my $generator = \&pod_for_usage;
+
+my @names = sort keys %docs;
+my $line = "\t[, [[, ";
+for (my $i = 0; $i < $#names; $i++) {
+       if (length ($line.$names[$i]) >= 65) {
+               print "$line\n\t";
+               $line = "";
+       }
+       $line .= "$names[$i], ";
+}
+print $line . $names[-1];
+
+print "\n\n=head1 COMMAND DESCRIPTIONS\n";
+print "\n=over 4\n\n";
+
+foreach my $applet (@names) {
+       print $generator->($applet, $docs{$applet});
+}
+
+exit 0;
+
+__END__
+
+=head1 NAME
+
+autodocifier.pl - generate docs for busybox based on usage.h
+
+=head1 SYNOPSIS
+
+autodocifier.pl [OPTION]... [FILE]...
+
+Example:
+
+    ( cat docs/busybox_header.pod; \
+      docs/autodocifier.pl usage.h; \
+      cat docs/busybox_footer.pod ) > docs/busybox.pod
+
+=head1 DESCRIPTION
+
+The purpose of this script is to automagically generate
+documentation for busybox using its usage.h as the original source
+for content.  It used to be that same content has to be duplicated
+in 3 places in slightly different formats -- F<usage.h>,
+F<docs/busybox.pod>.  This was tedious and error-prone, so it was
+decided that F<usage.h> would contain all the text in a
+machine-readable form, and scripts could be used to transform this
+text into other forms if necessary.
+
+F<autodocifier.pl> is one such script.  It is based on a script by
+Erik Andersen <andersen@codepoet.org> which was in turn based on a
+script by Mark Whitley <markw@codepoet.org>
+
+=head1 OPTIONS
+
+=over 4
+
+=item B<--help>
+
+This displays the help message.
+
+=item B<--pod>
+
+Generate POD (this is the default)
+
+=item B<--verbose>
+
+Be verbose (not implemented)
+
+=back
+
+=head1 FORMAT
+
+The following is an example of some data this script might parse.
+
+    #define length_trivial_usage \
+            "STRING"
+    #define length_full_usage \
+            "Prints out the length of the specified STRING."
+    #define length_example_usage \
+            "$ length Hello\n" \
+            "5\n"
+
+Each entry is a cpp macro that defines a string.  The macros are
+named systematically in the form:
+
+    $name_$type_usage
+
+$name is the name of the applet.  $type can be "trivial", "full", "notes",
+or "example".  Every documentation macro must end with "_usage".
+
+The definition of the types is as follows:
+
+=over 4
+
+=item B<trivial>
+
+This should be a brief, one-line description of parameters that
+the command expects.  This will be displayed when B<-h> is issued to
+a command.  I<REQUIRED>
+
+=item B<full>
+
+This should contain descriptions of each option.  This will also
+be displayed along with the trivial help if CONFIG_FEATURE_TRIVIAL_HELP
+is disabled.  I<REQUIRED>
+
+=item B<notes>
+
+This is documentation that is intended to go in the POD or SGML, but
+not be printed when a B<-h> is given to a command.  To see an example
+of notes being used, see init_notes_usage in F<usage.h>.  I<OPTIONAL>
+
+=item B<example>
+
+This should be an example of how the command is actually used.
+This will not be printed when a B<-h> is given to a command -- it
+will only be included in the POD or SGML documentation.  I<OPTIONAL>
+
+=back
+
+=head1 FILES
+
+F<usage.h>
+
+=head1 COPYRIGHT
+
+Copyright (c) 2001 John BEPPU.  All rights reserved.  This program is
+free software; you can redistribute it and/or modify it under the same
+terms as Perl itself.
+
+=head1 AUTHOR
+
+John BEPPU <b@ax9.org>
+
+=cut
+
+# $Id: autodocifier.pl,v 1.26 2004/04/06 15:26:25 andersen Exp $