a122b0b069ae60cf3433d08412986ecebfb83b55
[oweals/busybox.git] / docs / autodocifier.pl
1 #!/usr/bin/perl -w
2
3 use strict;
4 use Getopt::Long;
5
6 # collect lines continued with a '\' into an array
7 sub continuation {
8         my $fh = shift;
9         my @line;
10
11         while (<$fh>) {
12                 my $s = $_;
13                 $s =~ s/\\\s*$//;
14                 #$s =~ s/#.*$//;
15                 push @line, $s;
16                 last unless (/\\\s*$/);
17         }
18         return @line;
19 }
20
21 # regex && eval away unwanted strings from documentation
22 sub beautify {
23         my $text = shift;
24         $text =~ s/USAGE_NOT\w+\(.*?"\s*\)//sxg;
25         $text =~ s/USAGE_\w+\(\s*?(.*?)"\s*\)/$1"/sxg;
26         $text =~ s/"\s*"//sg;
27         my @line = split("\n", $text);
28         $text = join('',
29                 map {
30                         s/^\s*"//;
31                         s/"\s*$//;
32                         s/%/%%/g;
33                         s/\$/\\\$/g;
34                         eval qq[ sprintf(qq{$_}) ]
35                 } @line
36         );
37         return $text;
38 }
39
40 # generate POD for an applet
41 sub pod_for_usage {
42         my $name  = shift;
43         my $usage = shift;
44
45         # Sigh.  Fixup the known odd-name applets.
46         $name =~ s/dpkg_deb/dpkg-deb/g;
47         $name =~ s/fsck_minix/fsck.minix/g;
48         $name =~ s/mkfs_minix/mkfs.minix/g;
49         $name =~ s/run_parts/run-parts/g;
50         $name =~ s/start_stop_daemon/start-stop-daemon/g;
51
52         # make options bold
53         my $trivial = $usage->{trivial};
54         $trivial =~ s/(?<!\w)(-\w+)/B<$1>/sxg;
55         my @f0 =
56                 map { $_ !~ /^\s/ && s/(?<!\w)(-\w+)/B<$1>/g; $_ }
57                 split("\n", $usage->{full});
58
59         # add "\n" prior to certain lines to make indented
60         # lines look right
61         my @f1;
62         my $len = @f0;
63         for (my $i = 0; $i < $len; $i++) {
64                 push @f1, $f0[$i];
65                 if (($i+1) != $len && $f0[$i] !~ /^\s/ && $f0[$i+1] =~ /^\s/) {
66                         next if ($f0[$i] =~ /^$/);
67                         push(@f1, "") unless ($f0[$i+1] =~ /^\s*$/s);
68                 }
69         }
70         my $full = join("\n", @f1);
71
72         # prepare notes if they exist
73         my $notes = (defined $usage->{notes})
74                 ? "$usage->{notes}\n\n"
75                 : "";
76
77         # prepare examples if they exist
78         my $example = (defined $usage->{example})
79                 ?
80                         "Example:\n\n" .
81                         join ("\n",
82                         map  { "\t$_" }
83                         split("\n", $usage->{example})) . "\n\n"
84                 : "";
85
86         return
87                 "=item B<$name>".
88                 "\n\n$name $trivial\n\n".
89                 "$full\n\n"   .
90                 "$notes"  .
91                 "$example" .
92                 "-------------------------------".
93                 "\n\n"
94         ;
95 }
96
97 # the keys are applet names, and
98 # the values will contain hashrefs of the form:
99 #
100 # {
101 #     trivial => "...",
102 #     full    => "...",
103 #     notes   => "...",
104 #     example => "...",
105 # }
106 my %docs;
107
108
109 # get command-line options
110
111 my %opt;
112
113 GetOptions(
114         \%opt,
115         "help|h",
116         "pod|p",
117         "verbose|v",
118 );
119
120 if (defined $opt{help}) {
121         print
122                 "$0 [OPTION]... [FILE]...\n",
123                 "\t--help\n",
124                 "\t--pod\n",
125                 "\t--verbose\n",
126         ;
127         exit 1;
128 }
129
130
131 # collect documenation into %docs
132
133 foreach (@ARGV) {
134         open(USAGE, $_) || die("$0: $_: $!");
135         my $fh = *USAGE;
136         my ($applet, $type, @line);
137         while (<$fh>) {
138                 if (/^#define (\w+)_(\w+)_usage/) {
139                         $applet = $1;
140                         $type   = $2;
141                         @line   = continuation($fh);
142                         my $doc = $docs{$applet} ||= { };
143                         my $text      = join("\n", @line);
144                         $doc->{$type} = beautify($text);
145                 }
146         }
147 }
148
149
150 # generate structured documentation
151
152 my $generator = \&pod_for_usage;
153
154 my @names = sort keys %docs;
155 print "\t[, [[, ";
156 for (my $i = 0; $i < $#names; $i++) {
157         if (($i + 2) % 8 == 0) {
158                 print "\n\t";
159         }
160         print "$names[$i], ";
161 }
162 print $names[-1];
163
164 print "\n\n=head1 COMMAND DESCRIPTIONS\n";
165 print "\n=over 4\n\n";
166
167 foreach my $applet (@names) {
168         print $generator->($applet, $docs{$applet});
169 }
170
171 exit 0;
172
173 __END__
174
175 =head1 NAME
176
177 autodocifier.pl - generate docs for busybox based on usage.h
178
179 =head1 SYNOPSIS
180
181 autodocifier.pl [OPTION]... [FILE]...
182
183 Example:
184
185     ( cat docs/busybox_header.pod; \
186       docs/autodocifier.pl usage.h; \
187       cat docs/busybox_footer.pod ) > docs/busybox.pod
188
189 =head1 DESCRIPTION
190
191 The purpose of this script is to automagically generate
192 documentation for busybox using its usage.h as the original source
193 for content.  It used to be that same content has to be duplicated
194 in 3 places in slightly different formats -- F<usage.h>,
195 F<docs/busybox.pod>.  This was tedious and error-prone, so it was
196 decided that F<usage.h> would contain all the text in a
197 machine-readable form, and scripts could be used to transform this
198 text into other forms if necessary.
199
200 F<autodocifier.pl> is one such script.  It is based on a script by
201 Erik Andersen <andersen@codepoet.org> which was in turn based on a
202 script by Mark Whitley <markw@codepoet.org>
203
204 =head1 OPTIONS
205
206 =over 4
207
208 =item B<--help>
209
210 This displays the help message.
211
212 =item B<--pod>
213
214 Generate POD (this is the default)
215
216 =item B<--verbose>
217
218 Be verbose (not implemented)
219
220 =back
221
222 =head1 FORMAT
223
224 The following is an example of some data this script might parse.
225
226     #define length_trivial_usage \
227             "STRING"
228     #define length_full_usage \
229             "Prints out the length of the specified STRING."
230     #define length_example_usage \
231             "$ length Hello\n" \
232             "5\n"
233
234 Each entry is a cpp macro that defines a string.  The macros are
235 named systematically in the form:
236
237     $name_$type_usage
238
239 $name is the name of the applet.  $type can be "trivial", "full", "notes",
240 or "example".  Every documentation macro must end with "_usage".
241
242 The definition of the types is as follows:
243
244 =over 4
245
246 =item B<trivial>
247
248 This should be a brief, one-line description of parameters that
249 the command expects.  This will be displayed when B<-h> is issued to
250 a command.  I<REQUIRED>
251
252 =item B<full>
253
254 This should contain descriptions of each option.  This will also
255 be displayed along with the trivial help if CONFIG_FEATURE_TRIVIAL_HELP
256 is disabled.  I<REQUIRED>
257
258 =item B<notes>
259
260 This is documentation that is intended to go in the POD or SGML, but
261 not be printed when a B<-h> is given to a command.  To see an example
262 of notes being used, see init_notes_usage in F<usage.h>.  I<OPTIONAL>
263
264 =item B<example>
265
266 This should be an example of how the command is actually used.
267 This will not be printed when a B<-h> is given to a command -- it
268 will only be included in the POD or SGML documentation.  I<OPTIONAL>
269
270 =back
271
272 =head1 FILES
273
274 F<usage.h>
275
276 =head1 COPYRIGHT
277
278 Copyright (c) 2001 John BEPPU.  All rights reserved.  This program is
279 free software; you can redistribute it and/or modify it under the same
280 terms as Perl itself.
281
282 =head1 AUTHOR
283
284 John BEPPU <b@ax9.org>
285
286 =cut
287
288 # $Id: autodocifier.pl,v 1.26 2004/04/06 15:26:25 andersen Exp $