summary.pl 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402
  1. #!/usr/bin/perl
  2. # Generate the Summary of Library Facilities (summary.texi).
  3. # Copyright (C) 2017-2026 Free Software Foundation, Inc.
  4. # This file is part of the GNU C Library.
  5. # The GNU C Library is free software; you can redistribute it and/or
  6. # modify it under the terms of the GNU Lesser General Public License
  7. # as published by the Free Software Foundation; either version 2.1 of
  8. # the License, or (at your option) any later version.
  9. # The GNU C Library is distributed in the hope that it will be useful,
  10. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  12. # Lesser General Public License for more details.
  13. # You should have received a copy of the GNU Lesser General Public
  14. # License along with the GNU C Library; if not, see
  15. # <https://www.gnu.org/licenses/>.
  16. # Anything declared in a header or defined in a standard should have
  17. # its origins annotated using the @standards macro (see macro.texi).
  18. # This script checks all such elements in the manual (generally,
  19. # @def|item*-commands), ensuring annotations are present and correct.
  20. # If any errors are detected, they are all reported at the end and
  21. # failure is indicated.
  22. use strict;
  23. use warnings;
  24. use locale;
  25. use File::Basename;
  26. $| = 1;
  27. my $script = basename $0;
  28. &help if $ARGV[0] eq "--help"; # Will exit(0).
  29. my @texis = @ARGV;
  30. # Various regexes.
  31. my $nde = qr/^\@node /;
  32. my $def = qr/^\@def/;
  33. my $itm = qr/^\@item /;
  34. my $itms = qr/^\@itemx? /; # Don't match @itemize.
  35. my $ann = qr/^\@(def\w+|item)x? /; # Annotatable.
  36. my $std = qr/^\@standards\{/;
  37. my $stx = qr/^\@standardsx\{/;
  38. my $stds = qr/^\@standardsx?\{/;
  39. my $strict_std = qr/^\@standards\{([^,]+, )[^,\}]+\}$/;
  40. my $strict_stx = qr/^\@standardsx\{([^,]+, ){2}[^,\}]+\}$/;
  41. my $lcon = qr/([vf]?table|itemize|enumerate)/;
  42. my $list = qr/^\@${lcon}/;
  43. my $endl = qr/^\@end ${lcon}/;
  44. my $ign = qr/^\@ignore/;
  45. my $eig = qr/^\@end ignore/;
  46. # Global scope.
  47. my $node;
  48. our $texi;
  49. my $input;
  50. my %entries;
  51. my %errors;
  52. for $texi (@texis) {
  53. open $input, '<', $texi or die "open $texi: $!";
  54. while (my $line = <$input>) {
  55. if ($line =~ $nde) {
  56. $node = &get_node($line);
  57. } elsif ($line =~ $def) {
  58. &process_annotation($line);
  59. } elsif ($line =~ $list) {
  60. &process_list($1); # @items occur in list or table context.
  61. } elsif ($line =~ $stds) {
  62. &record_error("Misplaced annotation", ["[$.] ".$line]);
  63. } elsif ($line =~ $ign) {
  64. while (<$input> !~ $eig) {}
  65. }
  66. }
  67. close $input or die "close $texi: $!";
  68. }
  69. # Disabled until annotations are complete.
  70. &print_errors() if %errors && 0; # Will exit(1).
  71. print("\@c DO NOT EDIT THIS FILE!\n".
  72. "\@c This file is generated by $script from the Texinfo sources.\n".
  73. "\@c The \@items are \@include'd from a \@table in header.texi.\n\n");
  74. &print_entry($_) for sort keys %entries;
  75. # Processes an annotatable element, including any subsequent elements
  76. # in an @*x chain, ensuring @standards are present, with valid syntax,
  77. # either recording any errors detected or creating Summary entries.
  78. # This function is the heart of the script.
  79. #
  80. # Prototypes and standards are gathered into separate lists and used
  81. # to evaluate the completeness and correctness of annotations before
  82. # generating the Summary entries. "Prototype" is used to refer to an
  83. # element's entire definition while avoiding conflation with
  84. # @def*-commands. "Element" is strictly used here to refer to the
  85. # name extracted from the prototype, as used in @standardsx, for
  86. # sorting the Summary.
  87. sub process_annotation
  88. {
  89. my $line = shift;
  90. my (@prototypes, @standards, $i, @tmp);
  91. # Gather prototypes and standards.
  92. push @prototypes, $line;
  93. while ($line = <$input>) {
  94. last if $line !~ $ann;
  95. push @prototypes, $line;
  96. }
  97. if ($line !~ $stds) { # The fundamental error.
  98. return &record_error('Missing annotation', \@prototypes);
  99. }
  100. push @standards, $line;
  101. push @standards, $line while ($line = <$input>) =~ $stds;
  102. # If next line is an @item, seek back to catch it on the next
  103. # iteration. This avoids imposing a non-Texinfo syntax
  104. # requirement of blank lines between consecutive annotated @items.
  105. if ($line =~ $itm) {
  106. seek $input, -length($line), 1 or die "seek: $!";
  107. }
  108. # Strict check for syntax errors. Other matches are loose, which
  109. # aids error detection and reporting by ensuring things that look
  110. # like standards aren't simply passed over, but caught here.
  111. for ($i=0; $i<@standards; ++$i) {
  112. my $standard = $standards[$i];
  113. if ($standard !~ $strict_std && $standard !~ $strict_stx) {
  114. push @tmp, $standard;
  115. }
  116. }
  117. return &record_error('Invalid syntax', \@tmp) if @tmp;
  118. # @standardsx should not be in non-@*x chains.
  119. if (@prototypes == 1) {
  120. for ($i=0; $i<@standards; ++$i) {
  121. return &record_error('Misplaced @standardsx', \@prototypes)
  122. if $standards[$i] =~ $stx;
  123. }
  124. }
  125. # @standards may only occur once in @*x chains, at the beginning.
  126. if (@prototypes > 1) {
  127. for ($i=1; $i<@standards; ++$i) {
  128. return &record_error('Misplaced @standards', \@prototypes)
  129. if $standards[$i] =~ $std;
  130. }
  131. }
  132. # The @standards are aligned.
  133. &add_entries(\@prototypes, \@standards);
  134. }
  135. # Goes through the prototypes, cleaning them up and extracting the
  136. # elements, pairing them with the appropriate annotations to create
  137. # Summary entries.
  138. sub add_entries
  139. {
  140. my ($prototypes, $standards) = @_;
  141. my $isx = @{$prototypes} > 1 ? 1 : 0;
  142. my $allx = $standards->[0] =~ $stx ? 1 : 0;
  143. my ($defstd, $defhdr, %standardsx, $i, $j);
  144. # Grab the default annotation and index any @standardsx. Take
  145. # care in case there is no default.
  146. if ($isx) {
  147. if (!$allx) {
  148. ($defstd, $defhdr)
  149. = $standards->[0] =~ /${std}([^,]+), (.*)\}$/;
  150. }
  151. for ($i = $allx ? 0 : 1; $i<@{$standards}; ++$i) {
  152. my ($e, $s, $h)
  153. = $standards->[$i] =~ /${stx}([^,]+), ([^,]+), (.*)\}$/;
  154. push @{$standardsx{$e}{hs}}, [$h, $s];
  155. }
  156. }
  157. for ($i=0; $i<@{$prototypes}; ++$i) {
  158. my $e = &get_element($prototypes->[$i]);
  159. my $p = &get_prototype($prototypes->[$i]);
  160. my ($s, $h);
  161. if ($isx && exists $standardsx{$e}) {
  162. for ($j=0; $j<@{$standardsx{$e}{hs}}; ++$j) {
  163. $h = $standardsx{$e}{hs}[$j]->[0];
  164. $s = $standardsx{$e}{hs}[$j]->[1];
  165. &record_entry($e, $p, $h, $s, $node);
  166. ++$standardsx{$e}{seen};
  167. }
  168. } elsif ($isx && $allx) {
  169. &record_error('Missing annotation', [$prototypes->[$i]]);
  170. } elsif ($isx) {
  171. &record_entry($e, $p, $defhdr, $defstd, $node);
  172. } else {
  173. for ($j=0; $j<@{$standards}; ++$j) {
  174. ($s, $h) = $standards->[$j] =~ /${std}([^,]+), ([^,\}]+)\}$/;
  175. &record_entry($e, $p, $h, $s, $node);
  176. }
  177. }
  178. }
  179. # Check if there were any unmatched @standardsx.
  180. for my $e (keys %standardsx) {
  181. if (!exists $standardsx{$e}{seen}) {
  182. &record_error('Spurious @standardsx', [$e."\n"])
  183. }
  184. }
  185. }
  186. # Stores a Summary entry in %entries. May be called multiple times
  187. # per element if multiple header and standard annotations exist. Also
  188. # keys on prototypes, as some elements have multiple prototypes. See
  189. # isnan in arith.texi for one example.
  190. sub record_entry
  191. {
  192. my ($ele, $proto, $hdr, $std, $node) = @_;
  193. push @{$entries{$ele}{$proto}}, [$hdr, $std, $node];
  194. }
  195. # Processes list or table contexts, with nesting.
  196. sub process_list
  197. {
  198. my $type = shift;
  199. my $in_vtbl = $type eq "vtable" ? 1 : 0;
  200. while (my $line = <$input>) {
  201. if ($line =~ $itms) {
  202. next if ! $in_vtbl; # Not an annotatable context.
  203. &process_annotation($line);
  204. } elsif ($line =~ $def) {
  205. &process_annotation($line);
  206. } elsif ($line =~ $stds) {
  207. &record_error('Misplaced annotation', ["[$.] ".$line]);
  208. } elsif ($line =~ $endl) {
  209. return; # All done.
  210. } elsif ($line =~ $list) {
  211. &process_list($1); # Nested list.
  212. }
  213. }
  214. }
  215. # Returns the current node from an @node line. Used for referencing
  216. # from the Summary.
  217. sub get_node
  218. {
  219. my $line = shift;
  220. chomp $line;
  221. $line =~ s/$nde//;
  222. my ($n) = split ',', $line;
  223. return $n
  224. }
  225. # Returns the cleaned up prototype from @def|item* lines.
  226. sub get_prototype
  227. {
  228. my $dfn = shift;
  229. chomp $dfn;
  230. $dfn =~ s/\s+/ /g; # Collapse whitespace.
  231. $dfn =~ s/ \{([^\}]*)\} / $1 /g; # Remove grouping braces.
  232. $dfn =~ s/^\@\S+ //; # Remove @-command.
  233. $dfn =~ s/^Macro //i; # Scrape off cruft...
  234. $dfn =~ s/^Data Type //i;
  235. $dfn =~ s/^Variable //i;
  236. $dfn =~ s/^Deprecated Function //i;
  237. $dfn =~ s/^SVID Macro //i;
  238. $dfn =~ s/^Obsolete function //i;
  239. $dfn =~ s/^Constant //i;
  240. $dfn =~ s/^Type //i;
  241. $dfn =~ s/^Function //i;
  242. $dfn =~ s/^\{(.*)\}$/$1/; # Debrace yourself.
  243. $dfn =~ s/^\{([^\}]*)\} /$1 /; # These ones too.
  244. return $dfn;
  245. }
  246. # Returns an annotated element's name.
  247. #
  248. # Takes a line defining an annotatable element (e.g., @def|item*),
  249. # splitting it on whitespace. The element is generally detected as
  250. # the member immediately preceding the first parenthesized expression
  251. # (e.g., a function), or the last token in the list. Some additional
  252. # cleanup is applied to the element before returning it.
  253. sub get_element
  254. {
  255. my $i = 0;
  256. my @toks = split /\s+/, shift;
  257. # tzname array uses '['; don't match function pointers.
  258. ++$i while $toks[$i] && $toks[$i] !~ /^[\(\[](?!\*)/;
  259. $toks[$i-1] =~ s/^\*//; # Strip pointer type syntax.
  260. $toks[$i-1] =~ s/^\{?([^\}]+)\}?$/$1/; # Strip braces.
  261. $toks[$i-1] =~ s/^\(\*([^\)]+)\)$/$1/; # Function pointers.
  262. return $toks[$i-1];
  263. }
  264. # Records syntax errors detected in the manual related to @standards.
  265. # The @def|item*s are grouped by file, then errors, to make it easier
  266. # to track down exactly where and what the problems are.
  267. sub record_error
  268. {
  269. my ($err, $list) = @_;
  270. push @{$errors{$texi}{$err}}, $_ for (@{$list});
  271. return 0;
  272. }
  273. # Reports all detected errors and exits with failure. Indentation is
  274. # used for readability, and "ERROR" is used for visibility.
  275. sub print_errors
  276. {
  277. for $texi (sort keys %errors) {
  278. print STDERR "ERRORS in $texi:\n";
  279. for my $err (sort keys %{$errors{$texi}}) {
  280. print STDERR " $err:\n";
  281. print STDERR " $_" for (@{$errors{$texi}{$err}});
  282. }
  283. }
  284. print(STDERR "\nFor a description of expected syntax, see ".
  285. "\`$script --help'\n\n");
  286. exit 1;
  287. }
  288. # Prints an entry in the Summary.
  289. #
  290. # All the blank lines in summary.texi may seem strange at first, but
  291. # they have significant impact on how Texinfo renders the output.
  292. # Essentially, each line is its own paragraph. There is a @comment
  293. # with the element name, arguably unnecessary, but useful for seeing
  294. # the sorting order and extracted element names, and maintains the
  295. # format established by summary.awk. Each @item in the @table is the
  296. # prototype, which may be anything from just a variable name to a
  297. # function declaration. The body of each @item contains lines
  298. # annotating the headers and standards each element is declared
  299. # in/comes from, with a reference to the @node documenting the element
  300. # wrt. each header and standard combination.
  301. sub print_entry
  302. {
  303. my $element = shift;
  304. for my $prototype (sort keys %{$entries{$element}}) {
  305. print "\@comment $element\n\@item $prototype\n\n";
  306. for (@{$entries{$element}{$prototype}}) {
  307. my ($header, $standard, $node)
  308. = ($_->[0], $_->[1], $_->[2]);
  309. if ($header =~ /^\(none\)$/i) {
  310. $header = "\@emph{no header}";
  311. } elsif ($header =~ /\(optional\)$/) {
  312. $header =~ s/^(\S+) \((.*)\)$/\@file{$1} \@emph{$2}/;
  313. } elsif ($header ne '???') {
  314. $header = "\@file{$header}";
  315. }
  316. print "$header ($standard): \@ref{$node}.\n\n";
  317. }
  318. }
  319. }
  320. # Document the syntax of @standards.
  321. sub help
  322. {
  323. print "$script ";
  324. print <<'EOH';
  325. generates the Summary of Library Facilities (summary.texi)
  326. from @standards and @standardsx macros in the Texinfo sources (see
  327. macros.texi). While generating the Summary, it also checks that
  328. @standards are used, correctly.
  329. In general, any @def*-command or @item in a @vtable is considered
  330. annotatable. "Misplaced annotation" refers to @standards macros
  331. detected outside an annotatable context. "Missing annotation" refers
  332. to annotatable elements without @standards. @standards are expected
  333. to immediately follow the elements being annotated. In @*x lists,
  334. @standards sets the default annotation and may only occur as the first
  335. annotation ("Misplaced @standards"). @standardsx may not be used
  336. outside @*x lists ("Misplaced @standardsx"). "Spurious @standardsx"
  337. refers to otherwise valid @standardsx macros that were not matched to
  338. an element in an @*x list. "Invalid syntax" means just that.
  339. The syntax of @standards annotations is designed to accommodate
  340. multiple header and standards annotations, as necessary.
  341. Examples:
  342. @deftp FOO
  343. @standards{STD, HDR}
  344. @defvar BAR
  345. @standards{STD, HDR1}
  346. @standards{STD, HDR2}
  347. @deftypefun foo
  348. @deftypefunx fool
  349. @standards{STD, HDR}
  350. @item bar
  351. @itemx baz
  352. @standardsx{bar, STD1, HDR1}
  353. @standardsx{baz, STD1, HDR1}
  354. @standardsx{baz, STD2, HDR2}
  355. Note that @standardsx deviates from the usual Texinfo syntax in that
  356. it is optional and may be used without @standards.
  357. EOH
  358. ; exit 0;
  359. }