123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403 |
- #!/usr/bin/perl
- # Generate the Summary of Library Facilities (summary.texi).
- # Copyright (C) 2017-2019 Free Software Foundation, Inc.
- # This file is part of the GNU C Library.
- # Contributed by Rical Jasan <ricaljasan@pacific.net>, 2017.
- # The GNU C Library is free software; you can redistribute it and/or
- # modify it under the terms of the GNU Lesser General Public License
- # as published by the Free Software Foundation; either version 2.1 of
- # the License, or (at your option) any later version.
- # The GNU C Library is distributed in the hope that it will be useful,
- # but WITHOUT ANY WARRANTY; without even the implied warranty of
- # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- # Lesser General Public License for more details.
- # You should have received a copy of the GNU Lesser General Public
- # License along with the GNU C Library; if not, see
- # <http://www.gnu.org/licenses/>.
- # Anything declared in a header or defined in a standard should have
- # its origins annotated using the @standards macro (see macro.texi).
- # This script checks all such elements in the manual (generally,
- # @def|item*-commands), ensuring annotations are present and correct.
- # If any errors are detected, they are all reported at the end and
- # failure is indicated.
- use strict;
- use warnings;
- use locale;
- use File::Basename;
- $| = 1;
- my $script = basename $0;
- &help if $ARGV[0] eq "--help"; # Will exit(0).
- my @texis = @ARGV;
- # Various regexes.
- my $nde = qr/^\@node /;
- my $def = qr/^\@def/;
- my $itm = qr/^\@item /;
- my $itms = qr/^\@itemx? /; # Don't match @itemize.
- my $ann = qr/^\@(def\w+|item)x? /; # Annotatable.
- my $std = qr/^\@standards\{/;
- my $stx = qr/^\@standardsx\{/;
- my $stds = qr/^\@standardsx?\{/;
- my $strict_std = qr/^\@standards\{([^,]+, )[^,\}]+\}$/;
- my $strict_stx = qr/^\@standardsx\{([^,]+, ){2}[^,\}]+\}$/;
- my $lcon = qr/([vf]?table|itemize|enumerate)/;
- my $list = qr/^\@${lcon}/;
- my $endl = qr/^\@end ${lcon}/;
- my $ign = qr/^\@ignore/;
- my $eig = qr/^\@end ignore/;
- # Global scope.
- my $node;
- our $texi;
- my $input;
- my %entries;
- my %errors;
- for $texi (@texis) {
- open $input, '<', $texi or die "open $texi: $!";
- while (my $line = <$input>) {
- if ($line =~ $nde) {
- $node = &get_node($line);
- } elsif ($line =~ $def) {
- &process_annotation($line);
- } elsif ($line =~ $list) {
- &process_list($1); # @items occur in list or table context.
- } elsif ($line =~ $stds) {
- &record_error("Misplaced annotation", ["[$.] ".$line]);
- } elsif ($line =~ $ign) {
- while (<$input> !~ $eig) {}
- }
- }
- close $input or die "close $texi: $!";
- }
- # Disabled until annotations are complete.
- &print_errors() if %errors && 0; # Will exit(1).
- print("\@c DO NOT EDIT THIS FILE!\n".
- "\@c This file is generated by $script from the Texinfo sources.\n".
- "\@c The \@items are \@include'd from a \@table in header.texi.\n\n");
- &print_entry($_) for sort keys %entries;
- # Processes an annotatable element, including any subsequent elements
- # in an @*x chain, ensuring @standards are present, with valid syntax,
- # either recording any errors detected or creating Summary entries.
- # This function is the heart of the script.
- #
- # Prototypes and standards are gathered into separate lists and used
- # to evaluate the completeness and correctness of annotations before
- # generating the Summary entries. "Prototype" is used to refer to an
- # element's entire definition while avoiding conflation with
- # @def*-commands. "Element" is strictly used here to refer to the
- # name extracted from the prototype, as used in @standardsx, for
- # sorting the Summary.
- sub process_annotation
- {
- my $line = shift;
- my (@prototypes, @standards, $i, @tmp);
- # Gather prototypes and standards.
- push @prototypes, $line;
- while ($line = <$input>) {
- last if $line !~ $ann;
- push @prototypes, $line;
- }
- if ($line !~ $stds) { # The fundamental error.
- return &record_error('Missing annotation', \@prototypes);
- }
- push @standards, $line;
- push @standards, $line while ($line = <$input>) =~ $stds;
- # If next line is an @item, seek back to catch it on the next
- # iteration. This avoids imposing a non-Texinfo syntax
- # requirement of blank lines between consecutive annotated @items.
- if ($line =~ $itm) {
- seek $input, -length($line), 1 or die "seek: $!";
- }
- # Strict check for syntax errors. Other matches are loose, which
- # aids error detection and reporting by ensuring things that look
- # like standards aren't simply passed over, but caught here.
- for ($i=0; $i<@standards; ++$i) {
- my $standard = $standards[$i];
- if ($standard !~ $strict_std && $standard !~ $strict_stx) {
- push @tmp, $standard;
- }
- }
- return &record_error('Invalid syntax', \@tmp) if @tmp;
- # @standardsx should not be in non-@*x chains.
- if (@prototypes == 1) {
- for ($i=0; $i<@standards; ++$i) {
- return &record_error('Misplaced @standardsx', \@prototypes)
- if $standards[$i] =~ $stx;
- }
- }
- # @standards may only occur once in @*x chains, at the beginning.
- if (@prototypes > 1) {
- for ($i=1; $i<@standards; ++$i) {
- return &record_error('Misplaced @standards', \@prototypes)
- if $standards[$i] =~ $std;
- }
- }
- # The @standards are aligned.
- &add_entries(\@prototypes, \@standards);
- }
- # Goes through the prototypes, cleaning them up and extracting the
- # elements, pairing them with the appropriate annotations to create
- # Summary entries.
- sub add_entries
- {
- my ($prototypes, $standards) = @_;
- my $isx = @{$prototypes} > 1 ? 1 : 0;
- my $allx = $standards->[0] =~ $stx ? 1 : 0;
- my ($defstd, $defhdr, %standardsx, $i, $j);
- # Grab the default annotation and index any @standardsx. Take
- # care in case there is no default.
- if ($isx) {
- if (!$allx) {
- ($defstd, $defhdr)
- = $standards->[0] =~ /${std}([^,]+), (.*)\}$/;
- }
- for ($i = $allx ? 0 : 1; $i<@{$standards}; ++$i) {
- my ($e, $s, $h)
- = $standards->[$i] =~ /${stx}([^,]+), ([^,]+), (.*)\}$/;
- push @{$standardsx{$e}{hs}}, [$h, $s];
- }
- }
- for ($i=0; $i<@{$prototypes}; ++$i) {
- my $e = &get_element($prototypes->[$i]);
- my $p = &get_prototype($prototypes->[$i]);
- my ($s, $h);
- if ($isx && exists $standardsx{$e}) {
- for ($j=0; $j<@{$standardsx{$e}{hs}}; ++$j) {
- $h = $standardsx{$e}{hs}[$j]->[0];
- $s = $standardsx{$e}{hs}[$j]->[1];
- &record_entry($e, $p, $h, $s, $node);
- ++$standardsx{$e}{seen};
- }
- } elsif ($isx && $allx) {
- &record_error('Missing annotation', [$prototypes->[$i]]);
- } elsif ($isx) {
- &record_entry($e, $p, $defhdr, $defstd, $node);
- } else {
- for ($j=0; $j<@{$standards}; ++$j) {
- ($s, $h) = $standards->[$j] =~ /${std}([^,]+), ([^,\}]+)\}$/;
- &record_entry($e, $p, $h, $s, $node);
- }
- }
- }
- # Check if there were any unmatched @standardsx.
- for my $e (keys %standardsx) {
- if (!exists $standardsx{$e}{seen}) {
- &record_error('Spurious @standardsx', [$e."\n"])
- }
- }
- }
- # Stores a Summary entry in %entries. May be called multiple times
- # per element if multiple header and standard annotations exist. Also
- # keys on prototypes, as some elements have multiple prototypes. See
- # isnan in arith.texi for one example.
- sub record_entry
- {
- my ($ele, $proto, $hdr, $std, $node) = @_;
- push @{$entries{$ele}{$proto}}, [$hdr, $std, $node];
- }
- # Processes list or table contexts, with nesting.
- sub process_list
- {
- my $type = shift;
- my $in_vtbl = $type eq "vtable" ? 1 : 0;
- while (my $line = <$input>) {
- if ($line =~ $itms) {
- next if ! $in_vtbl; # Not an annotatable context.
- &process_annotation($line);
- } elsif ($line =~ $def) {
- &process_annotation($line);
- } elsif ($line =~ $stds) {
- &record_error('Misplaced annotation', ["[$.] ".$line]);
- } elsif ($line =~ $endl) {
- return; # All done.
- } elsif ($line =~ $list) {
- &process_list($1); # Nested list.
- }
- }
- }
- # Returns the current node from an @node line. Used for referencing
- # from the Summary.
- sub get_node
- {
- my $line = shift;
- chomp $line;
- $line =~ s/$nde//;
- my ($n) = split ',', $line;
- return $n
- }
- # Returns the cleaned up prototype from @def|item* lines.
- sub get_prototype
- {
- my $dfn = shift;
- chomp $dfn;
- $dfn =~ s/\s+/ /g; # Collapse whitespace.
- $dfn =~ s/ \{([^\}]*)\} / $1 /g; # Remove grouping braces.
- $dfn =~ s/^\@\S+ //; # Remove @-command.
- $dfn =~ s/^Macro //i; # Scrape off cruft...
- $dfn =~ s/^Data Type //i;
- $dfn =~ s/^Variable //i;
- $dfn =~ s/^Deprecated Function //i;
- $dfn =~ s/^SVID Macro //i;
- $dfn =~ s/^Obsolete function //i;
- $dfn =~ s/^Constant //i;
- $dfn =~ s/^Type //i;
- $dfn =~ s/^Function //i;
- $dfn =~ s/^\{(.*)\}$/$1/; # Debrace yourself.
- $dfn =~ s/^\{([^\}]*)\} /$1 /; # These ones too.
- return $dfn;
- }
- # Returns an annotated element's name.
- #
- # Takes a line defining an annotatable element (e.g., @def|item*),
- # splitting it on whitespace. The element is generally detected as
- # the member immediately preceding the first parenthesized expression
- # (e.g., a function), or the last token in the list. Some additional
- # cleanup is applied to the element before returning it.
- sub get_element
- {
- my $i = 0;
- my @toks = split /\s+/, shift;
- # tzname array uses '['; don't match function pointers.
- ++$i while $toks[$i] && $toks[$i] !~ /^[\(\[](?!\*)/;
- $toks[$i-1] =~ s/^\*//; # Strip pointer type syntax.
- $toks[$i-1] =~ s/^\{?([^\}]+)\}?$/$1/; # Strip braces.
- $toks[$i-1] =~ s/^\(\*([^\)]+)\)$/$1/; # Function pointers.
- return $toks[$i-1];
- }
- # Records syntax errors detected in the manual related to @standards.
- # The @def|item*s are grouped by file, then errors, to make it easier
- # to track down exactly where and what the problems are.
- sub record_error
- {
- my ($err, $list) = @_;
- push @{$errors{$texi}{$err}}, $_ for (@{$list});
- return 0;
- }
- # Reports all detected errors and exits with failure. Indentation is
- # used for readability, and "ERROR" is used for visibility.
- sub print_errors
- {
- for $texi (sort keys %errors) {
- print STDERR "ERRORS in $texi:\n";
- for my $err (sort keys %{$errors{$texi}}) {
- print STDERR " $err:\n";
- print STDERR " $_" for (@{$errors{$texi}{$err}});
- }
- }
- print(STDERR "\nFor a description of expected syntax, see ".
- "\`$script --help'\n\n");
- exit 1;
- }
- # Prints an entry in the Summary.
- #
- # All the blank lines in summary.texi may seem strange at first, but
- # they have significant impact on how Texinfo renders the output.
- # Essentially, each line is its own paragraph. There is a @comment
- # with the element name, arguably unnecessary, but useful for seeing
- # the sorting order and extracted element names, and maintains the
- # format established by summary.awk. Each @item in the @table is the
- # prototype, which may be anything from just a variable name to a
- # function declaration. The body of each @item contains lines
- # annotating the headers and standards each element is declared
- # in/comes from, with a reference to the @node documenting the element
- # wrt. each header and standard combination.
- sub print_entry
- {
- my $element = shift;
- for my $prototype (sort keys %{$entries{$element}}) {
- print "\@comment $element\n\@item $prototype\n\n";
- for (@{$entries{$element}{$prototype}}) {
- my ($header, $standard, $node)
- = ($_->[0], $_->[1], $_->[2]);
- if ($header =~ /^\(none\)$/i) {
- $header = "\@emph{no header}";
- } elsif ($header =~ /\(optional\)$/) {
- $header =~ s/^(\S+) \((.*)\)$/\@file{$1} \@emph{$2}/;
- } elsif ($header ne '???') {
- $header = "\@file{$header}";
- }
- print "$header ($standard): \@ref{$node}.\n\n";
- }
- }
- }
- # Document the syntax of @standards.
- sub help
- {
- print "$script ";
- print <<'EOH';
- generates the Summary of Library Facilities (summary.texi)
- from @standards and @standardsx macros in the Texinfo sources (see
- macros.texi). While generating the Summary, it also checks that
- @standards are used, correctly.
- In general, any @def*-command or @item in a @vtable is considered
- annotatable. "Misplaced annotation" refers to @standards macros
- detected outside an annotatable context. "Missing annotation" refers
- to annotatable elements without @standards. @standards are expected
- to immediately follow the elements being annotated. In @*x lists,
- @standards sets the default annotation and may only occur as the first
- annotation ("Misplaced @standards"). @standardsx may not be used
- outside @*x lists ("Misplaced @standardsx"). "Spurious @standardsx"
- refers to otherwise valid @standardsx macros that were not matched to
- an element in an @*x list. "Invalid syntax" means just that.
- The syntax of @standards annotations is designed to accomodate
- multiple header and standards annotations, as necessary.
- Examples:
- @deftp FOO
- @standards{STD, HDR}
- @defvar BAR
- @standards{STD, HDR1}
- @standards{STD, HDR2}
- @deftypefun foo
- @deftypefunx fool
- @standards{STD, HDR}
- @item bar
- @itemx baz
- @standardsx{bar, STD1, HDR1}
- @standardsx{baz, STD1, HDR1}
- @standardsx{baz, STD2, HDR2}
- Note that @standardsx deviates from the usual Texinfo syntax in that
- it is optional and may be used without @standards.
- EOH
- ; exit 0;
- }
|