#!/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;
+ $text =~ s/USAGE_NOT\w+\(.*?"\s*\)//sxg;
+ $text =~ s/USAGE_\w+\(\s*?(.*?)"\s*\)/$1"/sxg;
+ $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;
+
+ # make options bold
+ my $trivial = $usage->{trivial};
+ $trivial =~ s/(?<!\w)(-\w+)/B<$1>/sxg;
+ my @f0 =
+ map { $_ !~ /^\s/ && s/(?<!\w)(-\w+)/B<$1>/g; $_ }
+ split("\n", $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"
+ : "";
+
+ return
+ "=item B<$name>".
+ "\n\n" .
+ "$name $trivial".
+ "\n\n" .
+ $full .
+ "\n\n" .
+ $notes .
+ $example.
+ "-------------------------------".
+ "\n\n"
+ ;
+}
+
+# FIXME | generate SGML for an applet
+sub sgml_for_usage {
+ my $name = shift;
+ my $usage = shift;
+ return
+ "<fixme>\n".
+ " $name\n".
+ "</fixme>\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",
+ "sgml|s",
+ "pod|p",
+ "verbose|v",
+);
+
+if (defined $opt{help}) {
+ print
+ "$0 [OPTION]... [FILE]...\n",
+ "\t--help\n",
+ "\t--sgml\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;
+if (defined $opt{sgml}) {
+ $generator = \&sgml_for_usage;
+}
+
+foreach my $applet (sort keys %docs) {
+ 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>, and
+F<docs/busybox.sgml>. 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 was based on a script by Erik Andersen <andersen@lineo.com>
+which was in turn based on a script by Mark Whitley <markw@lineo.com>
+
+=head1 OPTIONS
+
+=over 4
+
+=item B<--help>
+
+This displays the help message.
+
+=item B<--pod>
+
+Generate POD (this is the default)
+
+=item B<--sgml>
+
+Generate SGML
+
+=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.23 2001/10/31 04:29:18 beppu Exp $