#!/usr/bin/env perl # # David Bateman Feb 02 2003 # # Extracts the help in texinfo format for particular function for use # in documentation. Based on make_index script from octave_forge. use strict; use File::Find; use File::Basename; use Text::Wrap; use FileHandle; use IPC::Open3; use POSIX ":sys_wait_h"; my $file = shift @ARGV; my $docfile = shift @ARGV; my $indexfile = shift @ARGV; my $line; if ( open(IN,$file) ) { $line = ; my $tex = 0; while ($line) { if ($line =~ /^\@DOCSTRING/) { my $found = 0; my $func = $line; $func =~ s/\@DOCSTRING\(//; $func =~ s/\)[\n\r]+//; my $func0 = $func; my $func1 = $func; $func0 =~ s/,.*$//; $func1 =~ s/^.*,//; if ( open(DOC,$docfile) ) { while () { next unless /\037/; my $function = $_; $function =~ s/\037//; $function =~ s/[\n\r]+//; if ($function =~ /^$func0$/) { my $desc = ""; my $docline; my $doctex = 0; while (($docline = ) && ($docline !~ /^\037/)) { $docline =~ s/^\s*-[*]- texinfo -[*]-\s*//; if ($docline =~ /\@tex/) { $doctex = 1; } if ($doctex) { $docline =~ s/\\\\/\\/g; } if ($docline =~ /\@end tex/) { $doctex = 0; } $desc .= $docline; } $desc =~ s/$func0/$func1/g; $desc =~ s/\@seealso\{(.*[^}])\}/See also: \1/g; print "$desc", "\n"; $found = 1; last; } } close (DOC); if (! $found) { print "\@emph{Not implemented}\n"; } } else { print STDERR "Could not open file $docfile\n"; exit 1; } } elsif ($line =~ /^\@REFERENCE_SECTION/) { my $secfound = 0; my $sec = $line; $sec =~ s/\@REFERENCE_SECTION\(//; $sec =~ s/\)[\n\r]+//; my @listfunc = (); my $nfunc = 0; my $seccat = 0; if ( open(IND,$indexfile) ) { while () { next unless /^[^ ]/; my $section = $_; $section =~ s/[\n\r]+//; if ($section =~ /^(.*?)\s*>>\s*(.*?)$/) { $section =~ s/.*>>(.*)/\1/; $seccat = 1; } $section =~ s/^ *//; $section =~ s/ *$//; if ($section =~ /^$sec$/) { if ($seccat) { print "\@iftex\n"; print "\@section Functions by Category\n"; # Get the list of categories to index my $firstcat = 1; my $category; while () { last if />>/; if (/^[^ ]/) { if (! $firstcat) { print "\@end table\n"; } else { $firstcat = 0; } $category = $_; $category =~ s/[\n\r]+//; print "\@subsection $category\n"; print "\@table \@asis\n"; } elsif (/^\s+(\S.*\S)\s*=\s*(\S.*\S)\s*$/) { my $func = $1; my $desc = $2; print "\@item $func\n"; print "$desc\n"; print "\n"; } else { if ($firstcat) { print STDERR "Error parsing index file\n"; exit 1; } s/^\s+//; my @funcs = split /\s+/; while ($#funcs >= 0) { my $func = shift @funcs; $func =~ s/^ *//; $func =~ s/[\n\r]+//; push @listfunc, $func; $nfunc = $nfunc + 1; print "\@item $func\n"; print func_summary($func, $docfile); print "\n"; } } } if (! $firstcat) { print "\@end table\n"; } print "\n\@section Functions Alphabetically\n"; print "\@end iftex\n\n"; } else { # Get the list of functions to index my $indline; while (($indline = ) && ($indline =~ /^ /)) { if ($indline =~ /^\s+(\S.*\S)\s*=\s*(\S.*\S)\s*$/) { next; } $indline =~ s/^\s+//; my @funcs = split(/\s+/,$indline); while ($#funcs >= 0) { my $func = shift @funcs; $func =~ s/^ *//; $func =~ s/[\n\r]+//; push @listfunc, $func; $nfunc = $nfunc + 1; } } } $secfound = 1; last; } } close (IND); if (! $secfound) { print STDERR "Did not find section $sec\n"; } } else { print STDERR "Could not open file $indexfile\n"; exit 1; } @listfunc = sort(@listfunc); my @listfunc2 = (); my $indent = 16 - 3; print "\@menu\n"; foreach my $func (@listfunc) { if ( open(DOC,$docfile) ) { my $found = 0; while () { next unless /\037/; my $function = $_; $function =~ s/\037//; $function =~ s/[\n\r]+//; if ($function =~ /^$func$/) { $found = 1; last; } } close (DOC); if ($found) { push @listfunc2, $func; my $func0 = "${func}::"; my $entry = sprintf("* %-*s %s",$indent,$func0,func_summary($func,$docfile)); print wrap("","\t\t","$entry"), "\n"; } } else { print STDERR "Could not open file $indexfile\n"; exit 1; } } print "\@end menu\n"; my $up = "Function Reference"; my $next; my $prev; my $mfunc = 1; foreach my $func (@listfunc2) { if ($mfunc == $nfunc) { $next = ""; } else { $next = @listfunc2[$mfunc]; $mfunc = $mfunc + 1; } print "\n\@node $func, $next, $prev, $up\n"; if ($seccat) { print "\@subsection $func\n\n"; } else { print "\@section $func\n\n"; } $prev = $func; my $found = 0; my $desc = ""; if ( open(DOC,$docfile) ) { while () { next unless /\037/; my $function = $_; $function =~ s/\037//; $function =~ s/[\n\r]+//; if ($function =~ /^$func$/) { my $docline; my $doctex = 0; while (($docline = ) && ($docline !~ /^\037/)) { $docline =~ s/^\s*-[*]- texinfo -[*]-\s*//; if ($docline =~ /\@tex/) { $doctex = 1; } if ($doctex) { $docline =~ s/\\\\/\\/g; } if ($docline =~ /\@end tex/) { $doctex = 0; } $desc .= $docline; } $desc =~ s/\@seealso\{(.*[^}])\}/See also: \1/g; print "$desc", "\n"; $found = 1; last; } } close (DOC); if (! $found) { print "\@emph{Not implemented}\n"; } } else { print STDERR "Could not open file $docfile\n"; exit 1; } } } else { if ($line =~ /\@tex/) { $tex = 1; } if ($tex) { $line =~ s/\\\\/\\/g; } print "$line"; if ($line =~ /\@end tex/) { $tex = 0; } } $line = ; } } else { print STDERR "Could not open file $file\n"; exit 1; } sub func_summary { # {{{1 my ($func, # in function name $docfile # in DOCSTRINGS ) = @_; my $desc = ""; my $found = 0; if ( open(DOC,$docfile) ) { while () { next unless /\037/; my $function = $_; $function =~ s/\037//; $function =~ s/[\n\r]+//; if ($function =~ /^$func$/) { my $docline; my $doctex = 0; while (($docline = ) && ($docline !~ /^\037/)) { if ($docline =~ /\@tex/) { $doctex = 1; } if ($doctex) { $docline =~ s/\\\\/\\/g; } if ($docline =~ /\@end tex/) { $doctex = 0; } $desc .= $docline; } $desc =~ s/\@seealso\{(.*[^}])\}/See also: \1/g; $found = 1; last; } } close (DOC); if (! $found) { $desc = "\@emph{Not implemented}"; } } else { print STDERR "Could not open file $docfile\n"; exit 1; } return first_sentence($desc); } # 1}}} sub first_sentence { # {{{1 # grab the first real sentence from the function documentation my ($desc) = @_; my $retval = ''; my $line; my $next; my @lines; my $trace = 0; # $trace = 1 if $desc =~ /Levenberg/; return "" unless defined $desc; if ($desc =~ /^\s*-[*]- texinfo -[*]-/) { # help text contains texinfo. Strip the indicator and run it # through makeinfo. (XXX FIXME XXX this needs to be a function) $desc =~ s/^\s*-[*]- texinfo -[*]-\s*//; my $cmd = "makeinfo --fill-column 1600 --no-warn --no-validate --no-headers --force --ifinfo"; open3(*Writer, *Reader, *Errer, $cmd) or die "Could not run info"; print Writer "\@macro seealso {args}\n\n\@noindent\nSee also: \\args\\.\n\@end macro\n"; print Writer "$desc"; close(Writer); @lines = ; close(Reader); my @err = ; close(Errer); waitpid(-1,&WNOHANG); # Display source and errors, if any if (@err) { my $n = 1; foreach $line ( split(/\n/,$desc) ) { printf "%2d: %s\n",$n++,$line; } print ">>> @err"; } # Print trace showing formatted output # print "\n"; # Skip prototype and blank lines while (1) { return "" unless @lines; $line = shift @lines; next if $line =~ /^\s*-/; next if $line =~ /^\s*$/; last; } } else { # print "\n"; # Skip prototype and blank lines @lines = split(/\n/,$desc); while (1) { return "" if ($#lines < 0); $line = shift @lines; next if $line =~ /^\s*[Uu][Ss][Aa][Gg][Ee]/; # skip " usage " $line =~ s/^\s*\w+\s*://; # chop " blah : " print "strip blah: $line\n" if $trace; $line =~ s/^\s*[Ff]unction\s+//; # chop " function " print "strip function $line\n" if $trace; $line =~ s/^\s*\[.*\]\s*=\s*//; # chop " [a,b] = " print "strip []= $line\n" if $trace; $line =~ s/^\s*\w+\s*=\s*//; # chop " a = " print "strip a= $line\n" if $trace; $line =~ s/^\s*\w+\s*\([^\)]*\)\s*//; # chop " f(x) " print "strip f(x) $line\n" if $trace; $line =~ s/^\s*[;:]\s*//; # chop " ; " print "strip ; $line\n" if $trace; $line =~ s/^\s*[[:upper:]][[:upper:]0-9_]+//; # chop " BLAH" print "strip BLAH $line\n" if $trace; $line =~ s/^\s*\w*\s*[-]+\s+//; # chop " blah --- " print "strip blah --- $line\n" if $trace; $line =~ s/^\s*\w+ *\t\s*//; # chop " blah " print "strip blah $line\n" if $trace; $line =~ s/^\s*\w+\s\s+//; # chop " blah " print "strip blah $line\n" if $trace; # next if $line =~ /^\s*\[/; # skip [a,b] = f(x) # next if $line =~ /^\s*\w+\s*(=|\()/; # skip a = f(x) OR f(x) next if $line =~ /^\s*or\s*$/; # skip blah \n or \n blah next if $line =~ /^\s*$/; # skip blank line next if $line =~ /^\s?!\//; # skip # !/usr/bin/octave # XXX FIXME XXX should be testing for unmatched () in proto # before going to the next line! last; } } # Try to make a complete sentence, including the '.' if ( "$line " !~ /[^.][.]\s/ && $#lines >= 0) { my $next = $lines[0]; $line =~ s/\s*$//; # trim trailing blanks on last $next =~ s/^\s*//; # trim leading blanks on next $line .= " $next" if "$next " =~ /[^.][.]\s/; # ends the sentence } # Tidy up the sentence. chomp $line; # trim trailing newline, if there is one $line =~ s/^\s*//; # trim leading blanks on line $line =~ s/([^.][.])\s.*$/$1/; # trim everything after the sentence print "Skipping:\n$desc---\n" if $line eq ""; # And return it. return $line; } # 1}}}