- documentation updates and a minor tweak to POD generation
[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/"//g;
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         # make options bold
46         my $trivial = $usage->{trivial};
47         $trivial =~ s/(?<!\w)(-\w+)/B<$1>/sxg;
48         my @f0 = 
49                 map { $_ !~ /^\s/ && s/(?<!\w)(-\w+)/B<$1>/g; $_ }
50                 split("\n", $usage->{full});
51
52         # add "\n" prior to certain lines to make indented
53         # lines look right
54         my @f1;
55         my $len = @f0;
56         for (my $i = 0; $i < $len; $i++) {
57                 push @f1, $f0[$i];
58                 if (($i+1) != $len && $f0[$i] !~ /^\s/ && $f0[$i+1] =~ /^\s/) {
59                         next if ($f0[$i] =~ /^$/);
60                         push(@f1, "") unless ($f0[$i+1] =~ /^\s*$/s);
61                 }
62         }
63         my $full = join("\n", @f1);
64
65         # prepare notes if they exists
66         my $notes = (defined $usage->{notes})
67                 ? "$usage->{notes}\n\n"
68                 : "";
69
70         # prepare example if one exists
71         my $example = (defined $usage->{example})
72                 ?  
73                         "Example:\n\n" .
74                         join ("\n", 
75                         map  { "\t$_" } 
76                         split("\n", $usage->{example})) . "\n\n"
77                 : "";
78
79         return
80                 "=item B<$name>".
81                 "\n\n"  .
82                 "$name $trivial".
83                 "\n\n"  .
84                 $full   .
85                 "\n\n"  .
86                 $notes  .
87                 $example.
88                 "-------------------------------".
89                 "\n\n"
90         ;
91 }
92
93 # FIXME | generate SGML for an applet
94 sub sgml_for_usage {
95         my $name  = shift;
96         my $usage = shift;
97         return
98                 "<fixme>\n".
99                 "  $name\n".
100                 "</fixme>\n"
101         ;
102 }
103
104 # the keys are applet names, and 
105 # the values will contain hashrefs of the form:
106 #
107 # {
108 #     trivial => "...",
109 #     full    => "...",
110 #     example => "...",
111 # }
112 my %docs;
113
114
115 # get command-line options
116
117 my %opt;
118
119 GetOptions(
120         \%opt,
121         "help|h",
122         "sgml|s",
123         "pod|p",
124         "verbose|v",
125 );
126
127 if (defined $opt{help}) {
128         print
129                 "$0 [OPTION]... [FILE]...\n",
130                 "\t--help\n",
131                 "\t--sgml\n",
132                 "\t--pod\n",
133                 "\t--verbose\n",
134         ;
135         exit 1;
136 }
137
138
139 # collect documenation into %docs
140
141 foreach (@ARGV) {
142         open(USAGE, $_) || die("$0: $_: $!");
143         my $fh = *USAGE;
144         my ($applet, $type, @line);
145         while (<$fh>) {
146                 if (/^#define (\w+)_(\w+)_usage/) {
147                         $applet = $1;
148                         $type   = $2;
149                         @line   = continuation($fh);
150                         my $doc = $docs{$applet} ||= { };
151                         my $text      = join("\n", @line);
152                         $doc->{$type} = beautify($text);
153                 }
154         }
155 }
156
157
158 # generate structured documentation
159
160 my $generator = \&pod_for_usage;
161 if (defined $opt{sgml}) {
162         $generator = \&sgml_for_usage;
163 }
164
165 foreach my $applet (sort keys %docs) {
166         print $generator->($applet, $docs{$applet});
167 }
168
169 exit 0;
170
171 __END__
172
173 =head1 NAME
174
175 autodocifier.pl - generate docs for busybox based on usage.h
176
177 =head1 SYNOPSIS
178
179 autodocifier.pl usage.h > something
180
181 =head1 DESCRIPTION
182
183 The purpose of this script is to automagically generate documentation
184 for busybox using its usage.h as the original source for content.
185 Currently, the same content has to be duplicated in 3 places in
186 slightly different formats -- F<usage.h>, F<docs/busybox.pod>, and
187 F<docs/busybox.sgml>.  This is tedious, so Perl has come to the rescue.
188
189 This script was based on a script by Erik Andersen (andersen@lineo.com).
190
191 =head1 OPTIONS
192
193 =over 4
194
195 =item B<--help>
196
197 This displays the help message.
198
199 =item B<--pod>
200
201 Generate POD (this is the default)
202
203 =item B<--sgml>
204
205 Generate SGML
206
207 =item B<--verbose>
208
209 Be verbose (not implemented)
210
211 =back
212
213 =head1 FORMAT
214
215 The following is an example of some data this script might parse.
216
217     #define length_trivial_usage \
218             "STRING"
219     #define length_full_usage \
220             "Prints out the length of the specified STRING."
221     #define length_example_usage \
222             "$ length "Hello"\n" \
223             "5\n"
224
225 Each entry is a cpp macro that defines a string.  The macros are
226 named systematically in the form:
227
228     $name_$type_usage
229
230 $name is the name of the applet.  $type can be "trivial", "full", "notes",
231 or "example".  Every documentation macro must end with "_usage".
232
233 The definition of the types is as follows:
234
235 =over 4
236
237 =item B<trivial>
238
239 This should be a brief, one-line description of parameters that
240 the command expects.  This will be displayed when B<-h> is issued to
241 a command.  I<REQUIRED>
242
243 =item B<full>
244
245 This should contain descriptions of each option.  This will also
246 be displayed along with the trivial help if BB_FEATURE_TRIVIAL_HELP
247 is disabled.  I<REQUIRED>
248
249 =item B<notes>
250
251 This is documentation that is intended to go in the POD or SGML, but
252 not be output when a B<-h> is given to a command.  To see an example
253 of notes being used, see init_notes_usage.  I<OPTIONAL>
254
255 =item B<example>
256
257 This should be an example of how the command is acutally used.
258 I<OPTIONAL>
259
260 =back
261
262 =head1 FILES
263
264 F<usage.h>
265
266 =head1 COPYRIGHT
267
268 Copyright (c) 2001 John BEPPU.  All rights reserved.  This program is
269 free software; you can redistribute it and/or modify it under the same
270 terms as Perl itself.
271
272 =head1 AUTHOR
273
274 John BEPPU <beppu@lineo.com>
275
276 =cut
277
278 # $Id: autodocifier.pl,v 1.18 2001/04/05 19:35:17 beppu Exp $