1#!/usr/bin/perl
2
3## Copyright (c) 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 Simon Josefsson
4##                    added -texinfo, -listfunc
5##                    man page revamp
6##                    various improvements
7## Copyright (c) 2001, 2002, 2013 Nikos Mavrogiannopoulos
8##                    added -tex, -pkg-site
9## Copyright (c) 1998 Michael Zucchi
10## Copyright (c) 2013 Adam Sampson
11##                    made highlighting not depend on hash order, for Perl 5.18
12
13# This program is free software: you can redistribute it and/or modify
14# it under the terms of the GNU General Public License as published by
15# the Free Software Foundation, either version 3 of the License, or
16# (at your option) any later version.
17#
18# This program is distributed in the hope that it will be useful,
19# but WITHOUT ANY WARRANTY; without even the implied warranty of
20# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
21# GNU General Public License for more details.
22#
23# You should have received a copy of the GNU General Public License
24# along with this program.  If not, see <https://www.gnu.org/licenses/>.
25
26# This will read a C source code file and scan for embedded comments
27# in the style of gnome comments (+minor extensions - see below).
28
29# usage:
30# gdoc [ -docbook | -html | -text | -man | -tex | -texinfo | -listfunc ]
31#      [ -sourceversion verno ] [ -include file | -includefuncprefix ]
32#      [ -bugsto address ] [ -pkg-site website ]
33#      [ -seeinfo infonode ] [ -copyright notice ] [ -verbatimcopying ]
34#      [ -function funcname [ -function funcname ...] ] c file(s)s > outputfile
35#
36#  Set output format using one of -docbook, -html, -text, -man, -tex,
37#  -texinfo, or -listfunc.  Default is man.
38#
39#  -sourceversion
40#       Version number for source code, e.g. '1.0.4'.  Used in 'man' headers.
41#       Defaults to using current date.
42#
43#  -include FILE
44#       For man pages, mention #include <FILE.h> in the synopsis.
45#
46#  -includefuncprefix
47#       For man pages, mention a #include <FILE.h> in the synopsis.
48#       The FILE derived from the function prefix.  For example, a
49#       function gss_init_sec_context will generate an include
50#       statement of #include <gss.h>.
51#
52#  -bugsto address
53#       For man pages, include a section about reporting bugs and mention
54#       the given e-mail address, e.g 'bug-libidn@gnu.org'.
55#
56#  -pkg-site web-site
57#       For man pages when -bugsto is used, also include help URLs to the
58#       the project's home page.
59#
60#  -seeinfo infonode
61#       For man pages, include a section that point to an info manual
62#       for more information.
63#
64#  -copyright notice
65#       For man pages, include a copyright section with the given
66#       notice after a preamble.  Use, e.g., '2002, 2003 Simon Josefsson'.
67#
68#  -verbatimcopying
69#       For man pages, and when the -copyright parameter is used,
70#       add a licensing statement that say verbatim copying is permitted.
71#
72#  -function funcname
73#	If set, then only generate documentation for the given function(s).  All
74#	other functions are ignored.
75#
76#  c files - list of 'c' files to process
77#
78#  All output goes to stdout, with errors to stderr.
79
80#
81# format of comments.
82# In the following table, (...)? signifies optional structure.
83#                         (...)* signifies 0 or more structure elements
84# /**
85#  * function_name(:)? (- short description)?
86# (* @parameterx: (description of parameter x)?)*
87# (* a blank line)?
88#  * (Description:)? (Description of function)?
89#  * (Section header: (section description)? )*
90#  (*)?*/
91#
92# So .. the trivial example would be:
93#
94# /**
95#  * my_function
96#  **/
97#
98# If the Description: header tag is omitted, then there must be a blank line
99# after the last parameter specification.
100# e.g.
101# /**
102#  * my_function - does my stuff
103#  * @my_arg: its mine damnit
104#  *
105#  * Does my stuff explained.
106#  */
107#
108#  or, could also use:
109# /**
110#  * my_function - does my stuff
111#  * @my_arg: its mine damnit
112#  * Description: Does my stuff explained.
113#  */
114# etc.
115#
116# All descriptions can be multiline, apart from the short function description.
117#
118# All descriptive text is further processed, scanning for the following special
119# patterns, which are highlighted appropriately.
120#
121# 'funcname()' - function
122# '$ENVVAR' - environmental variable OBSOLETE (?)
123# '#struct_name' - name of a structure
124# '@parameter' - name of a parameter
125# '%CONST' - name of a constant.
126
127#
128# Extensions for LaTeX:
129#
130# 1. the symbol '->' will be replaced with a rightarrow
131# 2. x^y with ${x}^{y}$.
132# 3. xxx\: with xxx:
133
134use POSIX qw(strftime);
135
136# match expressions used to find embedded type information
137$type_constant = '(?<!\%)\%([A-Za-z0-9_]+)';
138$type_func = "([A-Za-z0-9_]+\\(\\))";
139$type_param = '(?<!\@)\@([A-Za-z0-9_]+)\s*';
140$type_struct = "\\\#([A-Za-z0-9_]+)";
141$type_env = "(\\\$[A-Za-z0-9_]+)";
142
143
144# Output conversion substitutions.
145#  One for each output format
146
147# these work fairly well
148@highlights_html = ( [$type_constant, '"<i>$1</i>"'],
149		     [$type_func, '"<b>$1</b>"'],
150		     [$type_struct, '"<i>$1</i>"'],
151		     [$type_param, '" <tt><b>$1</b></tt> "'],
152		     ['\%\%', '"\%"']
153		     );
154$blankline_html = "<p>";
155
156@highlights_texinfo = ( [$type_param, '" \@code{$1} "'],
157			[$type_constant, '"\@code{$1} "'],
158			[$type_func, '"\@code{$1} "'],
159			[$type_struct, '"\@code{$1} "'],
160			['\%\%', '"\%"'],
161			 );
162$blankline_texinfo = "";
163
164@highlights_tex = ( [$type_param, '" {\\\bf $1} "'],
165		[$type_constant, '"{\\\it $1}"'],
166		[$type_func, '"{\\\bf $1}"'],
167		[$type_struct, '"{\\\it $1}"'],
168		['\@\@', '"\@"']
169		      );
170$blankline_tex = "\\\\";
171
172# sgml, docbook format
173@highlights_sgml = ( [$type_constant, '"<replaceable class=\"option\">$1</replaceable>"'],
174		     [$type_func, '"<function>$1</function>"'],
175		     [$type_struct, '"<structname>$1</structname>"'],
176		     [$type_env, '"<envar>$1</envar>"'],
177		     [$type_param, '" <parameter>$1</parameter> "'] );
178$blankline_sgml = "</para><para>\n";
179
180# these are pretty rough
181@highlights_man = ( [$type_constant, '"\\\fB$1\\\fP"'],
182		    [$type_func, '"\\\fB$1\\\fP"'],
183		    [$type_struct, '"\\\fB$1\\\fP"'],
184		    [$type_param, '" \\\fI$1\\\fP "'],
185		    ['\%\%', '"\%"'],
186		    ['\@\@', '"\@"']);
187$blankline_man = "";
188
189# text-mode
190@highlights_text = ( [$type_constant, '"$1"'],
191		     [$type_func, '"$1"'],
192		     [$type_struct, '"$1"'],
193		     [$type_param, '"$1 "'],
194		     ['\%\%', '"\%"'],
195		     ['\@\@', '"\@"']);
196$blankline_text = "";
197my $lineprefix = "";
198
199my $function_found = 0;
200
201sub usage {
202    print "Usage: $0 [ -v ] [ -docbook | -html | -text | -man | -tex | -texinfo  -listfunc ]\n";
203    print "         [ -sourceversion verno ] [ -include file | -includefuncprefix ]\n";
204    print "         [ -bugsto address ] [ -seeinfo infonode ] [ -copyright notice]\n";
205    print "         [ -verbatimcopying ] [ -pkg-site website ]\n";
206    print "         [ -function funcname [ -function funcname ...] ]\n";
207    print "         c source file(s) > outputfile\n";
208    exit 1;
209}
210
211# read arguments
212if ($#ARGV==-1) {
213    usage();
214}
215
216$verbose = 0;
217$output_mode = "man";
218@highlights = @highlights_man;
219$blankline = $blankline_man;
220$modulename = "API Documentation";
221$sourceversion = strftime "%Y-%m-%d", localtime;
222$function_only = 0;
223while ($ARGV[0] =~ m/^-(.*)/) {
224    $cmd = shift @ARGV;
225    if ($cmd eq "-html") {
226	$output_mode = "html";
227	@highlights = @highlights_html;
228	$blankline = $blankline_html;
229    } elsif ($cmd eq "-man") {
230	$output_mode = "man";
231	@highlights = @highlights_man;
232	$blankline = $blankline_man;
233    } elsif ($cmd eq "-tex") {
234	$output_mode = "tex";
235	@highlights = @highlights_tex;
236	$blankline = $blankline_tex;
237    } elsif ($cmd eq "-texinfo") {
238	$output_mode = "texinfo";
239	@highlights = @highlights_texinfo;
240	$blankline = $blankline_texinfo;
241    } elsif ($cmd eq "-text") {
242	$output_mode = "text";
243	@highlights = @highlights_text;
244	$blankline = $blankline_text;
245    } elsif ($cmd eq "-docbook") {
246	$output_mode = "sgml";
247	@highlights = @highlights_sgml;
248	$blankline = $blankline_sgml;
249    } elsif ($cmd eq "-listfunc") {
250	$output_mode = "listfunc";
251    } elsif ($cmd eq "-module") { # not needed for sgml, inherits from calling document
252	$modulename = shift @ARGV;
253    } elsif ($cmd eq "-sourceversion") {
254	$sourceversion = shift @ARGV;
255    } elsif ($cmd eq "-include") {
256	$include = shift @ARGV;
257    } elsif ($cmd eq "-includefuncprefix") {
258	$includefuncprefix = 1;
259    } elsif ($cmd eq "-bugsto") {
260	$bugsto = shift @ARGV;
261    } elsif ($cmd eq "-pkg-site") {
262	$pkgsite = shift @ARGV;
263    } elsif ($cmd eq "-copyright") {
264	$copyright = shift @ARGV;
265    } elsif ($cmd eq "-verbatimcopying") {
266	$verbatimcopying = 1;
267    } elsif ($cmd eq "-seeinfo") {
268	$seeinfo = shift @ARGV;
269    } elsif ($cmd eq "-function") { # to only output specific functions
270	$function_only = 1;
271	$function = shift @ARGV;
272	$function_table{$function} = 1;
273    } elsif ($cmd eq "-v") {
274	$verbose = 1;
275    } elsif (($cmd eq "-h") || ($cmd eq "--help")) {
276	usage();
277    }
278}
279
280##
281# dumps section contents to arrays/hashes intended for that purpose.
282#
283sub dump_section {
284    my $name = shift @_;
285    my $contents = join "\n", @_;
286
287    $name = " $name";
288
289    if ($name =~ m/$type_constant/) {
290	$name = $1;
291#	print STDERR "constant section '$1' = '$contents'\n";
292	$constants{$name} = $contents;
293    } elsif ($name =~ m/$type_param/) {
294#	print STDERR "parameter def '$1' = '$contents'\n";
295	$name = $1;
296	$parameters{$name} = $contents;
297    } else {
298#	print STDERR "other section '$name' = '$contents'\n";
299	$name =~ tr/ //d;
300	$sections{$name} = $contents;
301	push @sectionlist, $name;
302    }
303}
304
305##
306# output function
307#
308# parameters, a hash.
309#  function => "function name"
310#  parameterlist => @list of parameters
311#  parameters => %parameter descriptions
312#  sectionlist => @list of sections
313#  sections => %section descriptions
314#
315
316sub just_highlight {
317    my $contents = join "\n", @_;
318    my $line;
319    my $ret = "";
320
321    foreach $highlight (@highlights) {
322	my ($pattern, $replace) = @$highlight;
323	#print "scanning pattern $pattern ($replace)\n";
324	$contents =~ s/$pattern/$replace/gees;
325    }
326    foreach $line (split "\n", $contents) {
327	if ($line eq ""){
328	    $ret = $ret . $lineprefix . $blankline;
329	} else {
330	    $ret = $ret . $lineprefix . $line;
331	}
332	$ret = $ret . "\n";
333    }
334
335    return $ret;
336}
337
338sub output_highlight {
339    print (just_highlight (@_));
340}
341
342# output in texinfo
343sub output_texinfo {
344    my %args = %{$_[0]};
345    my ($parameter, $section);
346    my $count;
347
348    print "\@subheading ".$args{'function'}."\n";
349    print "\@anchor{".$args{'function'}."}\n";
350    print "\@deftypefun {" . $args{'functiontype'} . "} ";
351    print "{".$args{'function'}."} ";
352    print "(";
353    $count = 0;
354    foreach $parameter (@{$args{'parameterlist'}}) {
355	print $args{'parametertypes'}{$parameter}." \@var{".$parameter."}";
356	if ($count != $#{$args{'parameterlist'}}) {
357	    $count++;
358	    print ", ";
359	}
360    }
361    print ")\n";
362    foreach $parameter (@{$args{'parameterlist'}}) {
363	if ($args{'parameters'}{$parameter}) {
364	    print "\@var{".$parameter."}: ";
365	    output_highlight($args{'parameters'}{$parameter});
366	    print "\n";
367	}
368    }
369    foreach $section (@{$args{'sectionlist'}}) {
370	$section =~ s/\@//g;
371	print "\n\@strong{$section:} " if $section ne $section_default;
372	$args{'sections'}{$section} =~ s:([{}]):\@$1:gs;
373	output_highlight($args{'sections'}{$section});
374    }
375    print "\@end deftypefun\n\n";
376}
377
378sub output_enum_texinfo {
379    my %args = %{$_[0]};
380    my ($parameter, $section);
381    my $count;
382    my $name = $args{'enum'};
383    my $param;
384    my $param2;
385    my $sec;
386    my $check;
387    my $type;
388
389    print "\n\@c $name\n";
390    print "\@table \@code\n";
391
392    $check=0;
393    foreach $parameter (@{$args{'parameterlist'}}) {
394        $param1 = $parameter;
395	$param1 =~ s/_/_\@-/g;
396
397	$check = 1;
398	print "\@item ".$param1."\n";
399#	print "\n";
400
401        $param2 = $args{'parameters'}{$parameter};
402	$out = just_highlight($param2);
403	chomp $out;
404	print $out . "\n";
405    }
406    print "\@end table\n";
407}
408
409# output in html
410sub output_html {
411    my %args = %{$_[0]};
412    my ($parameter, $section);
413    my $count;
414    print "\n\n<a name=\"". $args{'function'} . "\">&nbsp</a><h2>Function</h2>\n";
415
416    print "<i>".$args{'functiontype'}."</i>\n";
417    print "<b>".$args{'function'}."</b>\n";
418    print "(";
419    $count = 0;
420    foreach $parameter (@{$args{'parameterlist'}}) {
421	print "<i>".$args{'parametertypes'}{$parameter}."</i> <b>".$parameter."</b>\n";
422	if ($count != $#{$args{'parameterlist'}}) {
423	    $count++;
424	    print ", ";
425	}
426    }
427    print ")\n";
428
429    print "<h3>Arguments</h3>\n";
430    print "<dl>\n";
431    foreach $parameter (@{$args{'parameterlist'}}) {
432	print "<dt><i>".$args{'parametertypes'}{$parameter}."</i> <b>".$parameter."</b>\n";
433	print "<dd>";
434	output_highlight($args{'parameters'}{$parameter});
435    }
436    print "</dl>\n";
437    foreach $section (@{$args{'sectionlist'}}) {
438	print "<h3>$section</h3>\n";
439	print "<ul>\n";
440	output_highlight($args{'sections'}{$section});
441	print "</ul>\n";
442    }
443    print "<hr>\n";
444}
445
446# output in tex
447sub output_tex {
448    my %args = %{$_[0]};
449    my ($parameter, $section);
450    my $count;
451    my $func = $args{'function'};
452    my $param;
453    my $param2;
454    my $sec;
455    my $check;
456    my $type;
457
458    $func =~ s/_/\\_/g;
459
460    print "\n\n\\begin{function}\n";
461    print "\\functionTitle{". $func . "}\n";
462    print "\\index{". $func . "}\n";
463
464    $type = $args{'functiontype'};
465    $type =~ s/_/\\_/g;
466
467    print "{\\it ".$type."}\n";
468    print "{\\bf ".$func."}\n";
469    print "(";
470    $count = 0;
471    foreach $parameter (@{$args{'parameterlist'}}) {
472        $param = $args{'parametertypes'}{$parameter};
473        $param2 = $parameter;
474	$param =~ s/_/\\_/g;
475        $param2 =~ s/_/\\_/g;
476
477	print "{\\it ".$param."} {\\bf ".$param2."}";
478	if ($count != $#{$args{'parameterlist'}}) {
479	    $count++;
480	    print ", ";
481	}
482    }
483    print ")\n";
484
485    print "\n\\begin{functionArguments}\n";
486
487    $check=0;
488    foreach $parameter (@{$args{'parameterlist'}}) {
489        $param1 = $args{'parametertypes'}{$parameter};
490        $param1 =~ s/_/\\_/g;
491        $param2 = $parameter;
492	$param2 =~ s/_/\\_/g;
493
494	$check = 1;
495	print "\\functionArgument {\\it ".$param1."} {\\bf ".$param2."}: \n";
496#	print "\n";
497
498	$param3 = $args{'parameters'}{$parameter};
499	$param3 =~ s/\#([a-zA-Z\_]+)/{\\it $1}/g;
500	$param3 =~ s/\%([a-zA-Z\_]+)/{\\bf $1}/g;
501
502	$out = just_highlight($param3);
503	$out =~ s/_/\\_/g;
504	print $out;
505    }
506    if ($check==0) {
507	print "\\item void\n";
508    }
509    print "\\end{functionArguments}\n";
510
511    foreach $section (@{$args{'sectionlist'}}) {
512	$sec = $section;
513	$sec =~ s/_/\\_/g;
514	$sec =~ s/#([a-zA-Z\_]+)/{\\it $1}/g;
515
516	print "\n\\begin{function${sec}}\n";
517	$out = $args{'sections'}{$section};
518
519	$out =~ s/\#([a-zA-Z\_]+)/{\\it $1}/g;
520	$out =~ s/\%([a-zA-Z\_]+)/{\\bf $1}/g;
521	$out =~ s/\@([a-zA-Z\_]+)/{\\bf $1}/g;
522	$out =~ s/_/\\_\\-/g;
523        $out =~ s/\$/\\\$/g;
524	$out =~ s/#/\\#/g;
525	$out =~ s/\n\n/\n/g;
526	$out =~ s/\\:/:/g;
527	$out =~ s/\-\>/\$\\rightarrow\$/g;
528	$out =~ s/([0-9]+)\^([0-9]+)/\$\{$1\}\^\{$2\}\$/g;
529
530	print $out;
531	print "\\end{function${sec}}\n";
532    }
533    print "\\end{function}\n\n";
534}
535
536sub output_enum_tex {
537    my %args = %{$_[0]};
538    my ($parameter, $section);
539    my $count;
540    my $name = $args{'enum'};
541    my $param;
542    my $param2;
543    my $sec;
544    my $check;
545    my $type;
546
547    print "\n\n\\begin{enum}\n";
548    $name =~ s/_/\\_/g;
549    print "\\enumTitle{". $name . "}\n";
550    print "\\index{". $name . "}\n";
551
552    print "\n\\begin{enumList}\n";
553
554    $check=0;
555    foreach $parameter (@{$args{'parameterlist'}}) {
556        $param1 = $parameter;
557	$param1 =~ s/_/\\_\\-/g;
558
559	$check = 1;
560	print "\\enumElement{".$param1."}{";
561#	print "\n";
562
563        $param2 = $args{'parameters'}{$parameter};
564	$param2 =~ s/\#([a-zA-Z\_]+)/{\\it $1}/g;
565	$param2 =~ s/\%([a-zA-Z\_]+)/{\\bf $1}/g;
566	$param2 =~ s/([0-9]+)\^([0-9]+)/\$\{$1\}\^\{$2\}\$/g;
567	$out = just_highlight($param2);
568	$out =~ s/_/\\_/g;
569	chomp $out;
570	print $out . "}\n";
571    }
572    print "\\end{enumList}\n";
573
574    print "\\end{enum}\n\n";
575}
576
577# output in sgml DocBook
578sub output_sgml {
579    my %args = %{$_[0]};
580    my ($parameter, $section);
581    my $count;
582    my $id;
583
584    $id = $args{'module'}."-".$args{'function'};
585    $id =~ s/[^A-Za-z0-9]/-/g;
586
587    print "<refentry>\n";
588    print "<refmeta>\n";
589    print "<refentrytitle><phrase id=\"$id\">".$args{'function'}."</phrase></refentrytitle>\n";
590    print "</refmeta>\n";
591    print "<refnamediv>\n";
592    print " <refname>".$args{'function'}."</refname>\n";
593    print " <refpurpose>\n";
594    print "  ".$args{'purpose'}."\n";
595    print " </refpurpose>\n";
596    print "</refnamediv>\n";
597
598    print "<refsynopsisdiv>\n";
599    print " <title>Synopsis</title>\n";
600    print "  <funcsynopsis>\n";
601    print "   <funcdef>".$args{'functiontype'}." ";
602    print "<function>".$args{'function'}." ";
603    print "</function></funcdef>\n";
604
605#    print "<refsect1>\n";
606#    print " <title>Synopsis</title>\n";
607#    print "  <funcsynopsis>\n";
608#    print "   <funcdef>".$args{'functiontype'}." ";
609#    print "<function>".$args{'function'}." ";
610#    print "</function></funcdef>\n";
611
612    $count = 0;
613    if ($#{$args{'parameterlist'}} >= 0) {
614	foreach $parameter (@{$args{'parameterlist'}}) {
615	    print "   <paramdef>".$args{'parametertypes'}{$parameter};
616	    print " <parameter>$parameter</parameter></paramdef>\n";
617	}
618    } else {
619	print "  <void>\n";
620    }
621    print "  </funcsynopsis>\n";
622    print "</refsynopsisdiv>\n";
623#    print "</refsect1>\n";
624
625    # print parameters
626    print "<refsect1>\n <title>Arguments</title>\n";
627#    print "<para>\nArguments\n";
628    if ($#{$args{'parameterlist'}} >= 0) {
629	print " <variablelist>\n";
630	foreach $parameter (@{$args{'parameterlist'}}) {
631	    print "  <varlistentry>\n   <term><parameter>$parameter</parameter></term>\n";
632	    print "   <listitem>\n    <para>\n";
633	    $lineprefix="     ";
634	    output_highlight($args{'parameters'}{$parameter});
635	    print "    </para>\n   </listitem>\n  </varlistentry>\n";
636	}
637	print " </variablelist>\n";
638    } else {
639	print " <para>\n  None\n </para>\n";
640    }
641    print "</refsect1>\n";
642
643    # print out each section
644    $lineprefix="   ";
645    foreach $section (@{$args{'sectionlist'}}) {
646	print "<refsect1>\n <title>$section</title>\n <para>\n";
647#	print "<para>\n$section\n";
648	if ($section =~ m/EXAMPLE/i) {
649	    print "<example><para>\n";
650	}
651	output_highlight($args{'sections'}{$section});
652#	print "</para>";
653	if ($section =~ m/EXAMPLE/i) {
654	    print "</para></example>\n";
655	}
656	print " </para>\n</refsect1>\n";
657    }
658
659    print "\n\n";
660}
661
662##
663# output in man
664sub output_man {
665    my %args = %{$_[0]};
666    my ($parameter, $section);
667    my $count;
668
669    print ".\\\" DO NOT MODIFY THIS FILE!  It was generated by gdoc.\n";
670    print ".TH \"$args{'function'}\" 3 \"$args{'sourceversion'}\" \"". $args{'module'} . "\" \"". $args{'module'} . "\"\n";
671
672    print ".SH NAME\n";
673
674    print $args{'function'};
675    if ($args{'purpose'}) {
676	print " \\- " . $args{'purpose'} . "\n";
677    } else {
678	print " \\- API function\n";
679    }
680
681    print ".SH SYNOPSIS\n";
682    print ".B #include <". $args{'include'} . ">\n"
683	if $args{'include'};
684    print ".B #include <". lc((split /_/, $args{'function'})[0]) . ".h>\n"
685	if $args{'includefuncprefix'};
686    print ".sp\n";
687    print ".BI \"".$args{'functiontype'}." ".$args{'function'}."(";
688    $count = 0;
689    foreach $parameter (@{$args{'parameterlist'}}) {
690	print $args{'parametertypes'}{$parameter}." \" ".$parameter." \"";
691	if ($count != $#{$args{'parameterlist'}}) {
692	    $count++;
693	    print ", ";
694	}
695    }
696    print ");\"\n";
697
698    print ".SH ARGUMENTS\n";
699    foreach $parameter (@{$args{'parameterlist'}}) {
700	print ".IP \"".$args{'parametertypes'}{$parameter}." ".$parameter."\" 12\n";
701	$param = $args{'parameters'}{$parameter};
702	$param =~ s/-/\\-/g;
703	output_highlight($param);
704    }
705    foreach $section (@{$args{'sectionlist'}}) {
706	print ".SH \"" . uc($section) . "\"\n";
707	$sec = $args{'sections'}{$section};
708	$sec =~ s/-/\\-/g;
709	output_highlight($sec);
710    }
711
712    if ($args{'bugsto'}) {
713	print ".SH \"REPORTING BUGS\"\n";
714	print "Report bugs to <". $args{'bugsto'} . ">.\n";
715        #print ".br\n";
716	#print "General guidelines for reporting bugs: https://www.gnu.org/gethelp/\n";
717        print ".br\n";
718	if ($args{'pkgsite'}) {
719	    print "Home page: " . $args{'pkgsite'} . "\n";
720	}
721	print "\n";
722    }
723
724    if ($args{'copyright'}) {
725	print ".SH COPYRIGHT\n";
726	print "Copyright \\(co ". $args{'copyright'} . ".\n";
727	if ($args{'verbatimcopying'}) {
728	    print ".br\n";
729	    print "Copying and distribution of this file, with or without modification,\n";
730	    print "are permitted in any medium without royalty provided the copyright\n";
731	    print "notice and this notice are preserved.\n";
732	}
733    }
734
735    if ($args{'seeinfo'}) {
736	print ".SH \"SEE ALSO\"\n";
737	print "The full documentation for\n";
738	print ".B " . $args{'module'} . "\n";
739	print "is maintained as a Texinfo manual.\n";
740	if ($args{'pkgsite'}) {
741		print "If the /usr/share/doc/". $args{'module'} . "/\n";
742		print "directory does not contain the HTML form visit\n";
743		print ".B\n";
744		print ".IP " . $args{'pkgsite'} . "/manual/\n";
745	}
746	print ".PP\n";
747    }
748}
749
750sub output_listfunc {
751    my %args = %{$_[0]};
752    print $args{'function'} . "\n";
753}
754
755##
756# output in text
757sub output_text {
758    my %args = %{$_[0]};
759    my ($parameter, $section);
760
761    print "Function = ".$args{'function'}."\n";
762    print "  return type: ".$args{'functiontype'}."\n\n";
763    foreach $parameter (@{$args{'parameterlist'}}) {
764	print " ".$args{'parametertypes'}{$parameter}." ".$parameter."\n";
765	print "    -> ".$args{'parameters'}{$parameter}."\n";
766    }
767    foreach $section (@{$args{'sectionlist'}}) {
768	print " $section:\n";
769	print "    -> ";
770	output_highlight($args{'sections'}{$section});
771    }
772}
773
774##
775# generic output function - calls the right one based
776# on current output mode.
777sub output_function {
778#    output_html(@_);
779    eval "output_".$output_mode."(\@_);";
780}
781
782sub output_enum {
783    eval "output_enum_".$output_mode."(\@_);";
784}
785
786
787##
788# takes a function prototype and spits out all the details
789# stored in the global arrays/hsahes.
790sub dump_function {
791    my $prototype = shift @_;
792
793    if ($prototype =~ m/^()([a-zA-Z0-9_~:]+)\s*\(([^\)]*)\)/ ||
794	$prototype =~ m/^(\w+)\s+([a-zA-Z0-9_~:]+)\s*\(([^\)]*)\)/ ||
795	$prototype =~ m/^(\w+\s*\*)\s*([a-zA-Z0-9_~:]+)\s*\(([^\)]*)\)/ ||
796	$prototype =~ m/^(\w+\s+\w+)\s+([a-zA-Z0-9_~:]+)\s*\(([^\)]*)\)/ ||
797	$prototype =~ m/^(\w+\s+\w+\s*\*)\s*([a-zA-Z0-9_~:]+)\s*\(([^\)]*)\)/)  {
798	$return_type = $1;
799	$function_name = $2;
800	$args = $3;
801
802	if ($return_type eq 'typedef') {
803	    return;
804	}
805
806#	print STDERR "ARGS = '$args'\n";
807
808	foreach $arg (split ',', $args) {
809	    # strip leading/trailing spaces
810	    $arg =~ s/^\s*//;
811	    $arg =~ s/\s*$//;
812#	    print STDERR "SCAN ARG: '$arg'\n";
813	    @args = split('\s', $arg);
814
815#	    print STDERR " -> @args\n";
816	    $param = pop @args;
817#	    print STDERR " -> @args\n";
818	    if ($param =~ m/^(\*+)(.*)/) {
819		$param = $2;
820		push @args, $1;
821	    }
822	    if ($param =~ m/^(.*)(\[[0-9]*\])$/) {
823		$param = $1;
824		push @args, $2;
825	    }
826#	    print STDERR " :> @args\n";
827	    $type = join " ", @args;
828
829	    if ((!defined($parameters{$param}) || $parameters{$param} eq "") && $param ne "void") {
830		$parameters{$param} = "-- undescribed --";
831		print STDERR "error: $lineno: Function parameter '$param' not described in '$function_name'\n";
832		exit 1;
833	    }
834
835	    push @parameterlist, $param;
836	    $parametertypes{$param} = $type;
837
838#	    print STDERR "param = '$param', type = '$type'\n";
839	}
840    } else {
841	print STDERR "error: $lineno: Cannot understand prototype: '$prototype'\n";
842	exit 1;
843    }
844
845    if ($function_only==0 || defined($function_table{$function_name})) {
846	$function_found=1;
847	output_function({'function' => $function_name,
848			 'module' => $modulename,
849			 'sourceversion' => $sourceversion,
850			 'include' => $include,
851			 'includefuncprefix' => $includefuncprefix,
852			 'bugsto' => $bugsto,
853			 'pkgsite' => $pkgsite,
854			 'copyright' => $copyright,
855			 'verbatimcopying' => $verbatimcopying,
856			 'seeinfo' => $seeinfo,
857			 'functiontype' => $return_type,
858			 'parameterlist' => \@parameterlist,
859			 'parameters' => \%parameters,
860			 'parametertypes' => \%parametertypes,
861			 'sectionlist' => \@sectionlist,
862			 'sections' => \%sections,
863			 'purpose' => $function_purpose
864			 });
865    }
866}
867
868sub dump_enum {
869    my $prototype = shift @_;
870
871    if (($prototype =~ m/^\s*typedef\s+enum\s*[a-zA-Z0-9_~:]*\s*\{([\-a-zA-Z0-9_~=,:\s\(\)\<]+)\s*\}\s*([a-zA-Z0-9_]+);.*/)) {
872#        || $prototype =~ m/^\s*enum\s+([a-zA-Z0-9_~:]+).*/) {
873        $args = $1;
874	$name = $2;
875
876	foreach $arg (split ',', $args) {
877	    # strip leading/trailing spaces
878	    $arg =~ s/^\s*//;
879	    $arg =~ s/\s*$//;
880	    $arg =~ s/([A-Za-z0-9_]+)\s*=.*/$1/g;
881#	    print STDERR "SCAN ARG: '$arg'\n";
882
883            next if $arg eq '';
884	    if ((!defined($parameters{$arg}) || $parameters{$arg} eq "")) {
885		$parameters{$arg} = "-- undescribed --";
886		print STDERR "warning: $lineno: Enumeration parameter '$arg' not described in '$name'\n";
887	    }
888
889	    push @parameterlist, $arg;
890
891#	    print STDERR "param = '$arg'\n";
892	}
893    } else {
894#	print STDERR "warning: $lineno: Cannot understand enumeration: '$prototype'\n";
895	return;
896    }
897
898    output_enum({'enum' => $name,
899			 'module' => $modulename,
900			 'sourceversion' => $sourceversion,
901			 'include' => $include,
902			 'includefuncprefix' => $includefuncprefix,
903			 'bugsto' => $bugsto,
904			 'pkgsite' => $pkgsite,
905			 'copyright' => $copyright,
906			 'verbatimcopying' => $verbatimcopying,
907			 'seeinfo' => $seeinfo,
908			 'functiontype' => $return_type,
909			 'parameterlist' => \@parameterlist,
910			 'parameters' => \%parameters,
911			 'parametertypes' => \%parametertypes,
912			 'sectionlist' => \@sectionlist,
913			 'sections' => \%sections,
914			 'purpose' => $function_purpose
915			 });
916}
917
918######################################################################
919# main
920# states
921# 0 - normal code
922# 1 - looking for function name
923# 2 - scanning field start.
924# 3 - scanning prototype.
925$state = 0;
926$section = "";
927
928$doc_special = "\@\%\$\#";
929
930$doc_start = "^/\\*\\*\$";
931$doc_end = "\\*/";
932$doc_com = "\\s*\\*\\s*";
933$doc_func = $doc_com."(\\w+):?";
934$doc_sect = $doc_com."([".$doc_special."[:upper:]][\\w]+):\\s*(.*)";
935$doc_content = $doc_com."(.*)";
936
937%constants = ();
938%parameters = ();
939@parameterlist = ();
940%sections = ();
941@sectionlist = ();
942
943$contents = "";
944$section_default = "Description";	# default section
945$section = $section_default;
946$enum = 0;
947
948$lineno = 0;
949
950foreach $file (@ARGV) {
951    if (!open(IN,"<$file")) {
952	print STDERR "Error: Cannot open file $file\n";
953	next;
954    }
955    while ($line = <IN>) {
956	$lineno++;
957
958	if ($state == 0) {
959	    if ($line =~ /$doc_start/o) {
960		$state = 1;		# next line is always the function name
961#	    print STDERR "XXX: start of doc comment\n";
962	    }
963	} elsif ($state == 1) {	# this line is the function name (always)
964	    if ($line =~ /$doc_func/o) {
965		$function = $1;
966		$state = 2;
967#	    print STDERR "XXX: start of doc comment, looking for prototype\n";
968
969		if ($line =~ /-\s*(.*)/) {
970		    $function_purpose = $1;
971		} else {
972		    $function_purpose = "";
973		}
974		if ($verbose) {
975		    print STDERR "Info($lineno): Scanning doc for $function\n";
976		}
977	    } else {
978		print STDERR "warning: $lineno: Cannot understand $_ on line $lineno",
979		" - I thought it was a doc line\n";
980		$state = 0;
981	    }
982	} elsif ($state == 2) {	# look for head: lines, and include content
983	    if ($line =~ /$doc_sect/o) {
984		$newsection = $1;
985		$newcontents = $2;
986
987		if ($contents ne '') {
988		    dump_section($section, $contents);
989		    $section = $section_default;
990		}
991
992		$contents = $newcontents;
993		if ($contents ne "") {
994		    $contents .= "\n";
995		}
996		$section = $newsection;
997	    } elsif ($line =~ /$doc_end/) {
998
999		if ($contents ne "") {
1000		    dump_section($section, $contents);
1001		    $section = $section_default;
1002		    $contents = "";
1003		}
1004
1005		$prototype = '';
1006		$state = 3;
1007	    } elsif ($line =~ /$doc_content/) {
1008		# miguel-style comment kludge, look for blank lines after
1009		# @parameter line to signify start of description
1010		if ($1 eq '' && $section =~ m/^@/) {
1011		    dump_section($section, $contents);
1012		    $section = $section_default;
1013		    $contents = "";
1014		} else {
1015		    $contents .= $1."\n";
1016		}
1017	    } else {
1018		# i don't know - bad line?  ignore.
1019		#print STDERR "warning: $lineno: Bad line: $_";
1020	    }
1021	} elsif ($state == 3) {	# scanning for function { (end of prototype)
1022	    if ($line =~ /([a-zA-Z\s]+)enum(.*)$/) {
1023	        $enum = 1;
1024	    }
1025
1026	    if ($line =~ m#\s*/\*\s+MACDOC\s*#io) {
1027	      # do nothing
1028	    }
1029	    elsif ($enum == 1 && $line =~ /(\s*\{).*/) {
1030		$prototype = "typedef enum {";
1031	    }
1032	    elsif ($line =~ /([^\{]*)/) {
1033		$prototype .= $1;
1034	    }
1035
1036	    if ($enum == 0 && $line =~ /;/) {
1037		$prototype =~ s@/\*.*?\*/@@gos;	# strip comments.
1038		$prototype =~ s@[\r\n]+@ @gos; # strip newlines/cr's.
1039		$prototype =~ s@^ +@@gos; # strip leading spaces
1040
1041		dump_function($prototype);
1042
1043		$function = "";
1044		%constants = ();
1045		%parameters = ();
1046		%parametertypes = ();
1047		@parameterlist = ();
1048		%sections = ();
1049		@sectionlist = ();
1050		$prototype = "";
1051		$enum = 0;
1052
1053		$state = 0;
1054	    }
1055	    elsif ($enum == 1 && $line =~ /\}/) {
1056		$prototype =~ s@/\*.*?\*/@@gos;	# strip comments.
1057		$prototype =~ s@[\r\n]+@ @gos; # strip newlines/cr's.
1058		$prototype =~ s@^ +@@gos; # strip leading spaces
1059
1060		dump_enum($prototype);
1061
1062		$function = "";
1063		%constants = ();
1064		%parameters = ();
1065		%parametertypes = ();
1066		@parameterlist = ();
1067		%sections = ();
1068		@sectionlist = ();
1069		$prototype = "";
1070		$enum = 0;
1071
1072		$state = 0;
1073	    }
1074
1075	}
1076    }
1077}
1078
1079if ($function_only != 0 && $function_found == 0) {
1080	print STDERR "error: could not find the expected function\n";
1081	exit 1;
1082}
1083