xref: /openbsd-src/gnu/usr.bin/perl/autodoc.pl (revision d13be5d47e4149db2549a9828e244d59dbc43f15)
1#!/usr/bin/perl -w
2#
3# Unconditionally regenerate:
4#
5#    pod/perlintern.pod
6#    pod/perlapi.pod
7#
8# from information stored in
9#
10#    embed.fnc
11#    plus all the .c and .h files listed in MANIFEST
12#
13# Has an optional arg, which is the directory to chdir to before reading
14# MANIFEST and *.[ch].
15#
16# This script is normally invoked as part of 'make all', but is also
17# called from from regen.pl.
18
19use strict;
20
21#
22# See database of global and static function prototypes in embed.fnc
23# This is used to generate prototype headers under various configurations,
24# export symbols lists for different platforms, and macros to provide an
25# implicit interpreter context argument.
26#
27
28my %docs;
29my %funcflags;
30my %macro = (
31	     ax => 1,
32	     items => 1,
33	     ix => 1,
34	     svtype => 1,
35	    );
36my %missing;
37
38my $curheader = "Unknown section";
39
40sub autodoc ($$) { # parse a file and extract documentation info
41    my($fh,$file) = @_;
42    my($in, $doc, $line);
43FUNC:
44    while (defined($in = <$fh>)) {
45	if ($in =~ /^#\s*define\s+([A-Za-z_][A-Za-z_0-9]+)\(/ &&
46	    ($file ne 'embed.h' || $file ne 'proto.h')) {
47	    $macro{$1} = $file;
48	    next FUNC;
49	}
50        if ($in=~ /^=head1 (.*)/) {
51            $curheader = $1;
52            next FUNC;
53        }
54	$line++;
55	if ($in =~ /^=for\s+apidoc\s+(.*?)\s*\n/) {
56	    my $proto = $1;
57	    $proto = "||$proto" unless $proto =~ /\|/;
58	    my($flags, $ret, $name, @args) = split /\|/, $proto;
59	    my $docs = "";
60DOC:
61	    while (defined($doc = <$fh>)) {
62		$line++;
63		last DOC if $doc =~ /^=\w+/;
64		if ($doc =~ m:^\*/$:) {
65		    warn "=cut missing? $file:$line:$doc";;
66		    last DOC;
67		}
68		$docs .= $doc;
69	    }
70	    $docs = "\n$docs" if $docs and $docs !~ /^\n/;
71
72	    # Check the consistency of the flags
73	    my ($embed_where, $inline_where);
74	    my ($embed_may_change, $inline_may_change);
75
76	    my $docref = delete $funcflags{$name};
77	    if ($docref and %$docref) {
78		$embed_where = $docref->{flags} =~ /A/ ? 'api' : 'guts';
79		$embed_may_change = $docref->{flags} =~ /M/;
80	    } else {
81		$missing{$name} = $file;
82	    }
83	    if ($flags =~ /m/) {
84		$inline_where = $flags =~ /A/ ? 'api' : 'guts';
85		$inline_may_change = $flags =~ /x/;
86
87		if (defined $embed_where && $inline_where ne $embed_where) {
88		    warn "Function '$name' inconsistency: embed.fnc says $embed_where, Pod says $inline_where";
89		}
90
91		if (defined $embed_may_change
92		    && $inline_may_change ne $embed_may_change) {
93		    my $message = "Function '$name' inconsistency: ";
94		    if ($embed_may_change) {
95			$message .= "embed.fnc says 'may change', Pod does not";
96		    } else {
97			$message .= "Pod says 'may change', embed.fnc does not";
98		    }
99		    warn $message;
100		}
101	    } elsif (!defined $embed_where) {
102		warn "Unable to place $name!\n";
103		next;
104	    } else {
105		$inline_where = $embed_where;
106		$flags .= 'x' if $embed_may_change;
107		@args = @{$docref->{args}};
108		$ret = $docref->{retval};
109	    }
110
111	    $docs{$inline_where}{$curheader}{$name}
112		= [$flags, $docs, $ret, $file, @args];
113
114	    if (defined $doc) {
115		if ($doc =~ /^=(?:for|head)/) {
116		    $in = $doc;
117		    redo FUNC;
118		}
119	    } else {
120		warn "$file:$line:$in";
121	    }
122	}
123    }
124}
125
126sub docout ($$$) { # output the docs for one function
127    my($fh, $name, $docref) = @_;
128    my($flags, $docs, $ret, $file, @args) = @$docref;
129    $name =~ s/\s*$//;
130
131    $docs .= "NOTE: this function is experimental and may change or be
132removed without notice.\n\n" if $flags =~ /x/;
133    $docs .= "NOTE: the perl_ form of this function is deprecated.\n\n"
134	if $flags =~ /p/;
135
136    print $fh "=item $name\nX<$name>\n$docs";
137
138    if ($flags =~ /U/) { # no usage
139	# nothing
140    } elsif ($flags =~ /s/) { # semicolon ("dTHR;")
141	print $fh "\t\t$name;\n\n";
142    } elsif ($flags =~ /n/) { # no args
143	print $fh "\t$ret\t$name\n\n";
144    } else { # full usage
145	print $fh "\t$ret\t$name";
146	print $fh "(" . join(", ", @args) . ")";
147	print $fh "\n\n";
148    }
149    print $fh "=for hackers\nFound in file $file\n\n";
150}
151
152sub output {
153    my ($podname, $header, $dochash, $missing, $footer) = @_;
154    my $filename = "pod/$podname.pod";
155    open my $fh, '>', $filename or die "Can't open $filename: $!";
156
157    print $fh <<"_EOH_", $header;
158-*- buffer-read-only: t -*-
159
160!!!!!!!   DO NOT EDIT THIS FILE   !!!!!!!
161This file is built by $0 extracting documentation from the C source
162files.
163
164_EOH_
165
166    my $key;
167    # case insensitive sort, with fallback for determinacy
168    for $key (sort { uc($a) cmp uc($b) || $a cmp $b } keys %$dochash) {
169	my $section = $dochash->{$key};
170	print $fh "\n=head1 $key\n\n=over 8\n\n";
171	# Again, fallback for determinacy
172	for my $key (sort { uc($a) cmp uc($b) || $a cmp $b } keys %$section) {
173	    docout($fh, $key, $section->{$key});
174	}
175	print $fh "\n=back\n";
176    }
177
178    if (@$missing) {
179        print $fh "\n=head1 Undocumented functions\n\n";
180	print $fh "These functions are currently undocumented:\n\n=over\n\n";
181	for my $missing (sort @$missing) {
182	    print $fh "=item $missing\nX<$missing>\n\n";
183	}
184	print $fh "=back\n\n";
185    }
186
187    print $fh $footer, <<'_EOF_';
188=cut
189
190 ex: set ro:
191_EOF_
192
193    close $fh or die "Can't close $filename: $!";
194}
195
196if (@ARGV) {
197    my $workdir = shift;
198    chdir $workdir
199        or die "Couldn't chdir to '$workdir': $!";
200}
201
202open IN, "embed.fnc" or die $!;
203
204while (<IN>) {
205    chomp;
206    next if /^:/;
207    while (s|\\\s*$||) {
208	$_ .= <IN>;
209	chomp;
210    }
211    s/\s+$//;
212    next if /^\s*(#|$)/;
213
214    my ($flags, $retval, $func, @args) = split /\s*\|\s*/, $_;
215
216    next unless $func;
217
218    s/\b(NN|NULLOK)\b\s+//g for @args;
219    $func =~ s/\t//g; # clean up fields from embed.pl
220    $retval =~ s/\t//;
221
222    $funcflags{$func} = {
223			 flags => $flags,
224			 retval => $retval,
225			 args => \@args,
226			};
227}
228
229my $file;
230# glob() picks up docs from extra .c or .h files that may be in unclean
231# development trees.
232my $MANIFEST = do {
233  local ($/, *FH);
234  open FH, "MANIFEST" or die "Can't open MANIFEST: $!";
235  <FH>;
236};
237
238for $file (($MANIFEST =~ /^(\S+\.c)\t/gm), ($MANIFEST =~ /^(\S+\.h)\t/gm)) {
239    open F, "< $file" or die "Cannot open $file for docs: $!\n";
240    $curheader = "Functions in file $file\n";
241    autodoc(\*F,$file);
242    close F or die "Error closing $file: $!\n";
243}
244
245for (sort keys %funcflags) {
246    next unless $funcflags{$_}{flags} =~ /d/;
247    warn "no docs for $_\n"
248}
249
250foreach (sort keys %missing) {
251    next if $macro{$_};
252    # Heuristics for known not-a-function macros:
253    next if /^[A-Z]/;
254    next if /^dj?[A-Z]/;
255
256    warn "Function '$_', documented in $missing{$_}, not listed in embed.fnc";
257}
258
259# walk table providing an array of components in each line to
260# subroutine, printing the result
261
262my @missing_api = grep $funcflags{$_}{flags} =~ /A/ && !$docs{api}{$_}, keys %funcflags;
263output('perlapi', <<'_EOB_', $docs{api}, \@missing_api, <<'_EOE_');
264=head1 NAME
265
266perlapi - autogenerated documentation for the perl public API
267
268=head1 DESCRIPTION
269X<Perl API> X<API> X<api>
270
271This file contains the documentation of the perl public API generated by
272embed.pl, specifically a listing of functions, macros, flags, and variables
273that may be used by extension writers.  The interfaces of any functions that
274are not listed here are subject to change without notice.  For this reason,
275blindly using functions listed in proto.h is to be avoided when writing
276extensions.
277
278Note that all Perl API global variables must be referenced with the C<PL_>
279prefix.  Some macros are provided for compatibility with the older,
280unadorned names, but this support may be disabled in a future release.
281
282Perl was originally written to handle US-ASCII only (that is characters
283whose ordinal numbers are in the range 0 - 127).
284And documentation and comments may still use the term ASCII, when
285sometimes in fact the entire range from 0 - 255 is meant.
286
287Note that Perl can be compiled and run under EBCDIC (See L<perlebcdic>)
288or ASCII.  Most of the documentation (and even comments in the code)
289ignore the EBCDIC possibility.
290For almost all purposes the differences are transparent.
291As an example, under EBCDIC,
292instead of UTF-8, UTF-EBCDIC is used to encode Unicode strings, and so
293whenever this documentation refers to C<utf8>
294(and variants of that name, including in function names),
295it also (essentially transparently) means C<UTF-EBCDIC>.
296But the ordinals of characters differ between ASCII, EBCDIC, and
297the UTF- encodings, and a string encoded in UTF-EBCDIC may occupy more bytes
298than in UTF-8.
299
300Also, on some EBCDIC machines, functions that are documented as operating on
301US-ASCII (or Basic Latin in Unicode terminology) may in fact operate on all
302256 characters in the EBCDIC range, not just the subset corresponding to
303US-ASCII.
304
305The listing below is alphabetical, case insensitive.
306
307_EOB_
308
309=head1 AUTHORS
310
311Until May 1997, this document was maintained by Jeff Okamoto
312<okamoto@corp.hp.com>.  It is now maintained as part of Perl itself.
313
314With lots of help and suggestions from Dean Roehrich, Malcolm Beattie,
315Andreas Koenig, Paul Hudson, Ilya Zakharevich, Paul Marquess, Neil
316Bowers, Matthew Green, Tim Bunce, Spider Boardman, Ulrich Pfeifer,
317Stephen McCamant, and Gurusamy Sarathy.
318
319API Listing originally by Dean Roehrich <roehrich@cray.com>.
320
321Updated to be autogenerated from comments in the source by Benjamin Stuhl.
322
323=head1 SEE ALSO
324
325L<perlguts>, L<perlxs>, L<perlxstut>, L<perlintern>
326
327_EOE_
328
329my @missing_guts = grep $funcflags{$_}{flags} !~ /A/ && !$docs{guts}{$_}, keys %funcflags;
330
331output('perlintern', <<'END', $docs{guts}, \@missing_guts, <<'END');
332=head1 NAME
333
334perlintern - autogenerated documentation of purely B<internal>
335		 Perl functions
336
337=head1 DESCRIPTION
338X<internal Perl functions> X<interpreter functions>
339
340This file is the autogenerated documentation of functions in the
341Perl interpreter that are documented using Perl's internal documentation
342format but are not marked as part of the Perl API. In other words,
343B<they are not for use in extensions>!
344
345END
346
347=head1 AUTHORS
348
349The autodocumentation system was originally added to the Perl core by
350Benjamin Stuhl. Documentation is by whoever was kind enough to
351document their functions.
352
353=head1 SEE ALSO
354
355L<perlguts>, L<perlapi>
356
357END
358